diff --git a/.github/workflows/build_and_test_run_fuzzer_benchmarks.py b/.github/workflows/build_and_test_run_fuzzer_benchmarks.py index 023ec75f2..a710d5588 100644 --- a/.github/workflows/build_and_test_run_fuzzer_benchmarks.py +++ b/.github/workflows/build_and_test_run_fuzzer_benchmarks.py @@ -92,7 +92,7 @@ def make_builds(benchmarks, fuzzer): # Sort benchmarks so that they get built in a deterministic order. fuzzer_benchmark_pairs = sorted(fuzzer_benchmark_pairs, key=lambda pair: pair[1]) - print('Building fuzzer-benchmark pairs: {}'.format(fuzzer_benchmark_pairs)) + print(f'Building fuzzer-benchmark pairs: {fuzzer_benchmark_pairs}') for _, benchmark in fuzzer_benchmark_pairs: make_target = get_make_target(fuzzer, benchmark) make_command = ['make', 'RUNNING_ON_CI=yes', '-j', make_target] @@ -116,7 +116,7 @@ def do_build(build_type, fuzzer, always_build): elif build_type == 'bug': benchmarks = benchmark_utils.get_bug_benchmarks() else: - raise Exception('Invalid build_type: %s' % build_type) + raise Exception(f'Invalid build_type: {build_type}') if always_build: # Always do a build if always_build is True. @@ -138,7 +138,7 @@ def do_build(build_type, fuzzer, always_build): def main(): """Build OSS-Fuzz or standard benchmarks with a fuzzer.""" if len(sys.argv) != 3: - print('Usage: %s ' % sys.argv[0]) + print(f'Usage: {sys.argv[0]} ') return 1 build_type = sys.argv[1] fuzzer = sys.argv[2] diff --git a/.pylintrc b/.pylintrc index 5918b3f2d..f727d250f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,28 +1,82 @@ # Generated using `.pylintrc. --generate-rcfile` and then modified. -[MASTER] +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) extension-pkg-whitelist= # Add files or directories to the blocklist. They should be base names, not # paths. -# -# Files under alembic are generated and are nonconforming. -ignore=alembic +ignore=CVS, + alembic, # Files under alembic are generated and are nonconforming. + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\' represents the directory delimiter on Windows systems, it +# can't be used as an escape character. +ignore-paths=fuzzers/libfuzzer_focus_.*, + fuzzers/.*_um_.*, + fuzzers/.*_muttfuzz, + fuzzers/libafl_text, + fuzzers/.*_cmplog_.*, + -# Add files or directories matching the regex patterns to the blocklist. The -# regex matches against base names, not paths. -ignore-patterns= +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. jobs=0 # Control the amount of potential inferred values when inferring a single @@ -30,15 +84,19 @@ jobs=0 # complex, nested conditions. limit-inference-results=100 -# List of plugins (as comma separated values of python modules names) to load, +# List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes -# Specify a configuration file. -#rcfile= +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.10 + +# Discover python modules and packages in the file system subtree. +recursive=no # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. @@ -48,32 +106,56 @@ suggestion-mode=yes # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if +# disable everything first and then re-enable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=parameter-unpacking, - unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, - old-ne-operator, - old-octal-literal, - import-star-module-level, - non-ascii-bytes-literal, - raw-checker-failed, +disable=raw-checker-failed, bad-inline-option, locally-disabled, file-ignored, @@ -81,67 +163,7 @@ disable=parameter-unpacking, useless-suppression, deprecated-pragma, use-symbolic-message-instead, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - no-absolute-import, - old-division, - dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, - indexing-exception, - raising-string, - reload-builtin, - oct-method, - hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, - eq-without-hash, - div-method, - idiv-method, - rdiv-method, - exception-message-attribute, - invalid-str-codec, - sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - deprecated-itertools-function, - deprecated-types-field, - next-method-defined, - dict-items-not-iterating, - dict-keys-not-iterating, - dict-values-not-iterating, - deprecated-operator-function, - deprecated-urllib-function, - xreadlines-attribute, - deprecated-sys-function, - exception-escape, - comprehension-escape, + raw-checker-failed, fixme, too-few-public-methods, duplicate-code, # Too many false positives. @@ -154,206 +176,43 @@ disable=parameter-unpacking, enable=c-extension-no-member -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=optparse.Values,sys.exit - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - q, - ex, - Run, - logger, - _, - df, - fs - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[FORMAT] +[IMPORTS] -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=\s*(# )?? +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= -# Maximum number of characters on a single line. -max-line-length=80 +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= -# Maximum number of lines in a module. -max-module-lines=1000 +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma, - dict-separator +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= [MISCELLANEOUS] @@ -363,6 +222,9 @@ notes=FIXME, XXX, TODO +# Regular expression of note tags to take in consideration. +notes-rgx= + [TYPECHECK] @@ -374,11 +236,7 @@ contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. -generated-members=experiment_df.fuzzer,session - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes +generated-members= # Tells whether to warn about missing members when the owner of the attribute # is inferred to be None. @@ -392,16 +250,16 @@ ignore-none=yes # the rest of the inferred objects. ignore-on-opaque-inference=yes +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. @@ -415,25 +273,22 @@ missing-member-hint-distance=1 # showing a hint for a missing member. missing-member-max-choices=1 +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 +# List of decorators that change the signature of a decorated function. +signature-mutators= -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package.. -spelling-dict= -# List of comma separated words that should not be checked. -spelling-ignore-words= +[LOGGING] -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging [VARIABLES] @@ -445,6 +300,9 @@ additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes +# List of names allowed to shadow builtins +allowed-redefined-builtins= + # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, @@ -454,8 +312,7 @@ callbacks=cb_, # not be used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. +# Argument names that match this expression will be ignored. ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. @@ -463,87 +320,60 @@ init-import=no # List of qualified module names which can have objects that can redefine # builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,io,builtins - - -[LOGGING] - -# Format style used to check logging format string. `old` means using % -# formatting, while `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[STRING] +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io -# This flag controls whether the implicit-str-concat-in-sequence should -# generate a warning on implicit string concatenation in sequences defined over -# several lines. -check-str-concat-over-line-jumps=no +[SPELLING] -[IMPORTS] +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 'python-enchant' package. +spelling-dict= -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec +# List of comma separated words that should not be checked. +spelling-ignore-words= -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled). -ext-import-graph= +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled). -import-graph= +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled). -int-import-graph= -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= +[STRING] -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=yes +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no -[CLASSES] -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp +[EXCEPTIONS] -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=BaseException, + Exception -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs +[DESIGN] +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= -[DESIGN] +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= # Maximum number of arguments for function / method. max-args=5 @@ -551,7 +381,7 @@ max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 -# Maximum number of boolean expressions in an if statement. +# Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 # Maximum number of branch for function / method body. @@ -576,8 +406,234 @@ max-statements=50 min-public-methods=2 -[EXCEPTIONS] +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=80 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=no + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + q, + ex, + Run, + logger, + _, + df, + fs + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[METHOD_ARGS] -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=Exception +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request diff --git a/analysis/coverage_data_utils.py b/analysis/coverage_data_utils.py index 90cb22d7c..30a7d2a40 100644 --- a/analysis/coverage_data_utils.py +++ b/analysis/coverage_data_utils.py @@ -107,7 +107,7 @@ def get_fuzzer_covered_branches(fuzzer: str, benchmark: str, filestore: str): logger.warning( 'covered_branches.json file: %s could not be copied.', src_file) return {} - with open(dst_file.name) as json_file: + with open(dst_file.name, encoding='utf-8') as json_file: return json.load(json_file) diff --git a/analysis/data_utils.py b/analysis/data_utils.py index 11f2fa090..5cf5463aa 100644 --- a/analysis/data_utils.py +++ b/analysis/data_utils.py @@ -45,7 +45,7 @@ def validate_data(experiment_df): missing_columns = expected_columns.difference(experiment_df.columns) if missing_columns: raise ValueError( - 'Missing columns in experiment data: {}'.format(missing_columns)) + f'Missing columns in experiment data: {missing_columns}') def drop_uninteresting_columns(experiment_df): @@ -239,7 +239,7 @@ def benchmark_rank_by_median(benchmark_snapshot_df, key='edges_covered'): def benchmark_rank_by_percent(benchmark_snapshot_df, key='edges_covered'): """Returns ranking of fuzzers based on median (normalized/%) coverage.""" assert benchmark_snapshot_df.time.nunique() == 1, 'Not a snapshot!' - max_key = "{}_percent_max".format(key) + max_key = f'{key}_percent_max' medians = benchmark_snapshot_df.groupby('fuzzer')[max_key].median().astype( int) return medians.sort_values(ascending=False) @@ -370,11 +370,11 @@ def add_relative_columns(experiment_df): for key in ['edges_covered', 'bugs_covered']: if key not in df.columns: continue - new_col = "{}_percent_max".format(key) + new_col = f'{key}_percent_max' df[new_col] = df[key] / df.groupby('benchmark')[key].transform( 'max') * 100.0 - new_col = "{}_percent_fmax".format(key) + new_col = f'{key}_percent_fmax' df[new_col] = df[key] / df.groupby(['benchmark', 'fuzzer' ])[key].transform('max') * 100 return df diff --git a/analysis/experiment_results.py b/analysis/experiment_results.py index 444a1587b..61c151b32 100644 --- a/analysis/experiment_results.py +++ b/analysis/experiment_results.py @@ -196,7 +196,7 @@ def _relative_summary_table(self, key_column='edges_covered'): pivot = pivot.style\ .background_gradient(axis=1, cmap=whbl, vmin=95, vmax=100)\ .highlight_max(axis=1, color='lightgreen')\ - .format("{:.2f}")\ + .format('{:.2f}')\ .apply(data_utils.underline_row, axis=1, subset=idx)\ .set_table_styles(self._SUMMARY_TABLE_STYLE) return pivot @@ -222,7 +222,7 @@ def found_bugs_summary_table(self): groups = groups.reset_index() pivot = groups.pivot(index='benchmark', columns='fuzzer', - values="crash_key") + values='crash_key') # save fuzzer names fuzzer_names = pivot.columns pivot['Total'] = self._full_experiment_df.groupby( @@ -250,7 +250,7 @@ def highlight_max(row): # Sort fuzzers left to right by FuzzerSum pivot = pivot.sort_values(by='FuzzerSum', axis=1, ascending=False) pivot = pivot.style\ - .format("{:.0f}")\ + .format('{:.0f}')\ .apply(highlight_max, axis=1, subset=fuzzer_names)\ .apply(data_utils.underline_row, axis=1, subset=idx)\ .set_table_styles(self._SUMMARY_TABLE_STYLE) diff --git a/analysis/plotting.py b/analysis/plotting.py index 14b27d573..06c64ad83 100644 --- a/analysis/plotting.py +++ b/analysis/plotting.py @@ -13,12 +13,12 @@ # limitations under the License. """Plotting functions.""" -import matplotlib.pyplot as plt -import matplotlib.colors as colors import numpy as np import Orange import seaborn as sns +from matplotlib import colors +from matplotlib import pyplot as plt from analysis import data_utils from common import experiment_utils @@ -36,11 +36,11 @@ def _formatted_hour_min(seconds): hours = int(seconds / 60 / 60) minutes = int(seconds / 60) % 60 if hours: - time_string += '%dh' % hours + time_string += f'{hours}h' if minutes: if hours: time_string += ':' - time_string += '%dm' % minutes + time_string += f'{minutes}m' return time_string @@ -54,7 +54,7 @@ def _formatted_title(benchmark_snapshot_df): stats_string += _formatted_hour_min(snapshot_time) trial_count = benchmark_snapshot_df.fuzzer.value_counts().min() - stats_string += ', %d trials/fuzzer' % trial_count + stats_string += f', {trial_count} trials/fuzzer' stats_string += ')' return stats_string @@ -109,7 +109,6 @@ def __init__(self, fuzzers, quick=False, logscale=False): self._quick = quick self._logscale = logscale - # pylint: disable=no-self-use def _write_plot_to_image(self, plot_function, data, @@ -126,7 +125,7 @@ def _write_plot_to_image(self, fig, axes = plt.subplots(figsize=figsize) try: plot_function(data, axes=axes, **kwargs) - fig.savefig(image_path, bbox_inches="tight") + fig.savefig(image_path, bbox_inches='tight') finally: plt.close(fig) @@ -257,10 +256,10 @@ def box_or_violin_plot(self, showmeans=True, meanprops=mean_props) - sns.stripplot(**common_args, size=3, color="black", alpha=0.6) + sns.stripplot(**common_args, size=3, color='black', alpha=0.6) axes.set_title(_formatted_title(benchmark_snapshot_df)) - ylabel = 'Reached {} coverage'.format('bug' if bugs else 'branch') + ylabel = f'Reached {"bug" if bugs else "branch"} coverage' axes.set(ylabel=ylabel) axes.set(xlabel='Fuzzer (highest median coverage on the left)') axes.set_xticklabels(axes.get_xticklabels(), @@ -339,7 +338,7 @@ def ranking_plot(self, benchmark_snapshot_df, axes=None, bugs=False): ax=axes) axes.set_title(_formatted_title(benchmark_snapshot_df)) - ylabel = 'Reached {} coverage'.format('bug' if bugs else 'branch') + ylabel = f'Reached {"bug" if bugs else "branch"} coverage' axes.set(ylabel=ylabel) axes.set(xlabel='Fuzzer (highest median coverage on the left)') axes.set_xticklabels(axes.get_xticklabels(), @@ -387,8 +386,8 @@ def _generic_heatmap_plot(values, axes, args, shrink_cbar=0.2): args['annot'] = False axis = sns.heatmap(values, ax=axes, **args) - axis.set_ylabel("") - axis.set_xlabel("") + axis.set_ylabel('') + axis.set_xlabel('') label_args = {'rotation': 0, 'horizontalalignment': 'right'} axis.set_yticklabels(axis.get_yticklabels(), **label_args) label_args = {'rotation': 270, 'horizontalalignment': 'right'} @@ -424,7 +423,7 @@ def _pvalue_heatmap_plot(self, p_values, axes=None, symmetric=False): heatmap_args = { 'cmap': cmap, 'mask': mask if symmetric else None, - 'fmt': ".3f", + 'fmt': '.3f', 'norm': norm } @@ -464,7 +463,7 @@ def _a12_heatmap_plot(self, a12_values, axes=None): 'vmax': 1.0, 'square': True, 'annot': True, - 'fmt': ".2f" + 'fmt': '.2f' } return self._generic_heatmap_plot(a12_values, axes, @@ -486,7 +485,7 @@ def write_critical_difference_plot(self, average_ranks, num_of_benchmarks, critical_difference) fig = plt.gcf() try: - fig.savefig(image_path, bbox_inches="tight") + fig.savefig(image_path, bbox_inches='tight') finally: plt.close(fig) diff --git a/analysis/stat_tests.py b/analysis/stat_tests.py index 4b15ea4a9..f6a2fbe8d 100644 --- a/analysis/stat_tests.py +++ b/analysis/stat_tests.py @@ -198,7 +198,8 @@ def a12(measurements_x, measurements_y): rank_x_sum = rank_x.sum() # A = (R1/n1 - (n1+1)/2)/n2 # formula (14) in Vargha and Delaney, 2000 # The formula to compute A has been transformed to minimize accuracy errors. - # See: http://mtorchiano.wordpress.com/2014/05/19/effect-size-of-r-precision/ + # See: + # http://mtorchiano.wordpress.com/2014/05/19/effect-size-of-r-precision/ a12_measure = (2 * rank_x_sum - x_size * (x_size + 1)) / ( 2 * y_size * x_size) # equivalent formula to avoid accuracy errors diff --git a/analysis/test_coverage_data_utils.py b/analysis/test_coverage_data_utils.py index 18b2b1c65..533d9b830 100644 --- a/analysis/test_coverage_data_utils.py +++ b/analysis/test_coverage_data_utils.py @@ -88,8 +88,8 @@ def test_get_benchmark_cov_dict(): benchmark_cov_dict = coverage_data_utils.get_benchmark_cov_dict( coverage_dict, benchmark) expected_cov_dict = { - "afl": {(0, 0, 3, 3), (0, 0, 2, 2), (0, 0, 1, 1)}, - "libfuzzer": {(0, 0, 4, 4), (0, 0, 3, 3), (0, 0, 2, 3), (0, 0, 1, 1)} + 'afl': {(0, 0, 3, 3), (0, 0, 2, 2), (0, 0, 1, 1)}, + 'libfuzzer': {(0, 0, 4, 4), (0, 0, 3, 3), (0, 0, 2, 3), (0, 0, 1, 1)} } assert expected_cov_dict == benchmark_cov_dict diff --git a/analysis/test_data_utils.py b/analysis/test_data_utils.py index 133c8ceba..63e995672 100644 --- a/analysis/test_data_utils.py +++ b/analysis/test_data_utils.py @@ -67,14 +67,14 @@ def create_experiment_data(experiment='test_experiment', def test_validate_data_empty(): experiment_df = pd.DataFrame() - with pytest.raises(ValueError, match="Empty"): + with pytest.raises(ValueError, match='Empty'): data_utils.validate_data(experiment_df) def test_validate_data_missing_columns(): experiment_df = create_experiment_data() experiment_df.drop(columns=['trial_id', 'time'], inplace=True) - with pytest.raises(ValueError, match="Missing columns.*trial_id"): + with pytest.raises(ValueError, match='Missing columns.*trial_id'): data_utils.validate_data(experiment_df) @@ -89,7 +89,7 @@ def test_clobber_experiments_data(): """Tests that clobber experiments data clobbers stale snapshots from earlier experiments.""" df = pd.concat( - create_experiment_data('experiment-%d' % experiment_num) + create_experiment_data(f'experiment-{experiment_num}') for experiment_num in range(3)) df.reset_index(inplace=True) @@ -146,7 +146,7 @@ def test_filter_max_time(): assert filtered_df.time.unique().tolist() == list(expected_times) -@pytest.mark.parametrize("threshold", [0.3, 0.8, 1.0]) +@pytest.mark.parametrize('threshold', [0.3, 0.8, 1.0]) def test_benchmark_snapshot_complete(threshold): """Tests that the snapshot data contains only the latest timestamp for all trials, in case all trials have the same lengths. This should happen @@ -168,7 +168,7 @@ def test_benchmark_snapshot_complete(threshold): @pytest.mark.parametrize( - "threshold, expected_snapshot_time, expected_trials_left", [ + 'threshold, expected_snapshot_time, expected_trials_left', [ (1.0, 5, 4), (0.8, 5, 4), (0.7, 7, 3), diff --git a/common/benchmark_utils.py b/common/benchmark_utils.py index 7eb3b1ee5..70b7a6ab0 100644 --- a/common/benchmark_utils.py +++ b/common/benchmark_utils.py @@ -64,18 +64,13 @@ def get_runner_image_url(experiment, benchmark, fuzzer, docker_registry): """Get the URL of the docker runner image for fuzzing the benchmark with fuzzer.""" tag = 'latest' if environment.get('LOCAL_EXPERIMENT') else experiment - return '{docker_registry}/runners/{fuzzer}/{benchmark}:{tag}'.format( - docker_registry=docker_registry, - fuzzer=fuzzer, - benchmark=benchmark, - tag=tag) + return f'{docker_registry}/runners/{fuzzer}/{benchmark}:{tag}' def get_builder_image_url(benchmark, fuzzer, docker_registry): """Get the URL of the docker builder image for fuzzing the benchmark with fuzzer.""" - return '{docker_registry}/builders/{fuzzer}/{benchmark}'.format( - docker_registry=docker_registry, fuzzer=fuzzer, benchmark=benchmark) + return f'{docker_registry}/builders/{fuzzer}/{benchmark}' def validate_name(benchmark): diff --git a/common/experiment_utils.py b/common/experiment_utils.py index 2bbd64837..48ff16300 100644 --- a/common/experiment_utils.py +++ b/common/experiment_utils.py @@ -80,19 +80,18 @@ def get_custom_seed_corpora_filestore_path(): def get_dispatcher_instance_name(experiment: str) -> str: """Returns a dispatcher instance name for an experiment.""" - return 'd-%s' % experiment + return f'd-{experiment}' def get_trial_instance_name(experiment: str, trial_id: int) -> str: """Returns a unique instance name for each trial of an experiment.""" - return 'r-%s-%d' % (experiment, trial_id) + return f'r-{experiment}-{trial_id}' def get_cycle_filename(basename: str, cycle: int) -> str: """Returns a filename for a file that is relevant to a particular snapshot cycle.""" - filename = basename + '-' + ('%04d' % cycle) - return filename + return f'{basename}-{cycle:04d}' def get_corpus_archive_name(cycle: int) -> str: @@ -124,13 +123,13 @@ def get_trial_dir(fuzzer, benchmark, trial_id): """Returns the unique directory for |fuzzer|, |benchmark|, and |trial_id|.""" benchmark_fuzzer_directory = get_benchmark_fuzzer_dir(benchmark, fuzzer) - trial_subdir = 'trial-%d' % trial_id + trial_subdir = f'trial-{trial_id}' return posixpath.join(benchmark_fuzzer_directory, trial_subdir) def get_benchmark_fuzzer_dir(benchmark, fuzzer): """Returns the directory for |benchmark| and |fuzzer|.""" - return '%s-%s' % (benchmark, fuzzer) + return f'{benchmark}-{fuzzer}' def get_trial_bucket_dir(fuzzer, benchmark, trial_id): diff --git a/common/filesystem.py b/common/filesystem.py index c3b0cdd3a..427e18583 100644 --- a/common/filesystem.py +++ b/common/filesystem.py @@ -40,8 +40,10 @@ def recreate_directory(directory, create_parents=True): os.mkdir(directory) +# TODO: Fix this function. def write(path, contents, open_flags='w'): """Opens file at |path| with |open_flags| and writes |contents| to it.""" + # pylint: disable=unspecified-encoding with open(path, open_flags) as file_handle: return file_handle.write(contents) @@ -54,7 +56,7 @@ def append(path, line): def read(path, open_flags='r'): """Opens file at |path| with |open_flags| reads it and then returns the result.""" - with open(path, open_flags) as file_handle: + with open(path, open_flags, encoding='utf-8') as file_handle: return file_handle.read() @@ -96,7 +98,7 @@ def replace_dir(src_dir, dst_dir, move=True): it.""" if not os.path.isdir(src_dir): raise NotADirectoryError( - 'src_dir must be a directory. %s is not a directory.' % src_dir) + f'src_dir must be a directory. {src_dir} is not a directory.') shutil.rmtree(dst_dir, ignore_errors=True) if move: shutil.move(src_dir, dst_dir) diff --git a/common/fuzzer_utils.py b/common/fuzzer_utils.py index 3d5cd84f1..049ba8ce3 100644 --- a/common/fuzzer_utils.py +++ b/common/fuzzer_utils.py @@ -118,7 +118,7 @@ def validate(fuzzer): return False # Try importing the fuzzer module. - module_name = 'fuzzers.{}.fuzzer'.format(fuzzer) + module_name = f'fuzzers.{fuzzer}.fuzzer' try: importlib.import_module(module_name) return True diff --git a/common/gcloud.py b/common/gcloud.py index 9dd627123..f213e7773 100644 --- a/common/gcloud.py +++ b/common/gcloud.py @@ -64,36 +64,36 @@ def create_instance(instance_name: str, instance_name, '--image-family=cos-stable', '--image-project=cos-cloud', - '--zone=%s' % config['cloud_compute_zone'], + f'--zone={config["cloud_compute_zone"]}', '--scopes=cloud-platform', ] if instance_type == InstanceType.DISPATCHER: command.extend([ - '--machine-type=%s' % DISPATCHER_MACHINE_TYPE, - '--boot-disk-size=%s' % DISPATCHER_BOOT_DISK_SIZE, - '--boot-disk-type=%s' % DISPATCHER_BOOT_DISK_TYPE, + f'--machine-type={DISPATCHER_MACHINE_TYPE}', + f'--boot-disk-size={DISPATCHER_BOOT_DISK_SIZE}', + f'--boot-disk-type={DISPATCHER_BOOT_DISK_TYPE}', ]) else: machine_type = config['runner_machine_type'] if machine_type is not None: - command.append('--machine-type=%s' % machine_type) + command.append(f'--machine-type={machine_type}') else: # Do this to support KLEE experiments. command.append([ - '--custom-memory=%s' % config['runner_memory'], - '--custom-cpu=%s' % config['runner_num_cpu_cores'] + f'--custom-memory={config["runner_memory"]}', + f'--custom-cpu={config["runner_num_cpu_cores"]}', ]) command.extend([ '--no-address', - '--boot-disk-size=%s' % RUNNER_BOOT_DISK_SIZE, + f'--boot-disk-size={RUNNER_BOOT_DISK_SIZE}', ]) if preemptible: command.append('--preemptible') if startup_script: command.extend( - ['--metadata-from-file', 'startup-script=' + startup_script]) + ['--metadata-from-file', f'startup-script={startup_script}']) result = new_process.execute(command, expect_zero=False, **kwargs) if result.retcode == 0: @@ -130,8 +130,10 @@ def run_local_instance(startup_script: Optional[str] = None) -> bool: """Does the equivalent of "create_instance" for local experiments, runs |startup_script| in the background.""" command = ['/bin/bash', startup_script] - subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - return new_process.ProcessResult(0, '', False).retcode == 0 + with subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT): + return new_process.ProcessResult(0, '', False) def create_instance_template(template_name, docker_image, env, project, zone): @@ -144,13 +146,13 @@ def create_instance_template(template_name, docker_image, env, project, zone): 'gcloud', 'compute', '--project', project, 'instance-templates', 'create-with-container', template_name, '--no-address', '--image-family=cos-stable', '--image-project=cos-cloud', - '--region=%s' % zone, '--scopes=cloud-platform', - '--machine-type=%s' % MEASURER_WORKER_MACHINE_TYPE, - '--boot-disk-size=%s' % MEASURER_WORKER_BOOT_DISK_SIZE, '--preemptible', + f'--region={zone}', '--scopes=cloud-platform', + f'--machine-type={MEASURER_WORKER_MACHINE_TYPE}', + f'--boot-disk-size={MEASURER_WORKER_BOOT_DISK_SIZE}', '--preemptible', '--container-image', docker_image ] for item in env.items(): - command.extend(['--container-env', '%s=%s' % item]) + command.extend(['--container-env', f'{item[0]}={item[1]}']) new_process.execute(command) return posixpath.join('https://www.googleapis.com/compute/v1/projects/', project, 'global', 'instanceTemplates', template_name) diff --git a/common/logs.py b/common/logs.py index 77fb5e8ad..d55efbf98 100644 --- a/common/logs.py +++ b/common/logs.py @@ -72,6 +72,7 @@ def initialize(name='fuzzbench', default_extras=None, log_level=logging.INFO): _set_instance_name(default_extras) _set_experiment(default_extras) + # pylint: disable=global-variable-not-assigned global _default_extras _default_extras.update(default_extras) diff --git a/common/new_process.py b/common/new_process.py index 957573279..fd54c5cab 100644 --- a/common/new_process.py +++ b/common/new_process.py @@ -92,6 +92,7 @@ def execute( # pylint: disable=too-many-locals,too-many-branches if kill_children: kwargs['preexec_fn'] = os.setsid + # pylint: disable=consider-using-with process = subprocess.Popen(command, *args, **kwargs) process_group_id = os.getpgid(process.pid) diff --git a/common/sanitizer.py b/common/sanitizer.py index a921e0674..be4964bc9 100644 --- a/common/sanitizer.py +++ b/common/sanitizer.py @@ -13,8 +13,8 @@ # limitations under the License. """Sanitizer helpers.""" -# Matches ClusterFuzz configuration. -# See https://github.com/google/clusterfuzz/blob/master/src/python/system/environment.py. +# Matches ClusterFuzz configuration. See +# https://github.com/google/clusterfuzz/blob/master/src/python/system/environment.py. SANITIZER_OPTIONS = { 'handle_abort': 2, 'handle_sigbus': 2, @@ -48,7 +48,7 @@ def _join_memory_tool_options(options): """Joins a dict holding memory tool options into a string that can be set in the environment.""" return ':'.join( - '%s=%s' % (key, str(value)) for key, value in sorted(options.items())) + f'{key}={str(value)}' for key, value in sorted(options.items())) def set_sanitizer_options(env, is_fuzz_run=False): diff --git a/common/test_filesystem.py b/common/test_filesystem.py index d044e746c..ad05b224e 100644 --- a/common/test_filesystem.py +++ b/common/test_filesystem.py @@ -31,7 +31,7 @@ def test_recreate_directory_existing(fs): new_directory = 'new-directory' os.mkdir(new_directory) new_file = os.path.join(new_directory, 'file') - with open(new_file, 'w') as file_handle: + with open(new_file, 'w', encoding='utf-8') as file_handle: file_handle.write('hi') filesystem.recreate_directory(new_directory) @@ -65,7 +65,7 @@ def test_copy(fs): dst = 'destination_file' filesystem.copy(src, dst, ignore_errors=True) assert os.path.exists(dst) - with open(dst) as file_handle: + with open(dst, encoding='utf-8') as file_handle: assert file_handle.read() == contents @@ -148,7 +148,7 @@ def _assert_has_source_dir_contents(directory): for idx in range(3): file_path = os.path.join(directory, str(idx)) assert os.path.exists(file_path) - with open(file_path) as file_handle: + with open(file_path, encoding='utf-8') as file_handle: assert file_handle.read() == 'srcfile' @@ -180,7 +180,8 @@ def test_make_dir_copy(fs): new_filename = 'new-file' copied_new_file_path = os.path.join(copy_dir, new_filename) assert not os.path.exists(copied_new_file_path) - with open(os.path.join(SOURCE_DIR, new_filename), 'w') as file_handle: + with open(os.path.join(SOURCE_DIR, new_filename), 'w', + encoding='utf-8') as file_handle: file_handle.write('') copy_dir = filesystem.make_dir_copy(SOURCE_DIR) _assert_has_source_dir_contents(copy_dir) diff --git a/common/test_gcloud.py b/common/test_gcloud.py index eb5e8e21c..f1e36217a 100644 --- a/common/test_gcloud.py +++ b/common/test_gcloud.py @@ -110,7 +110,7 @@ def test_create_instance_failed_create(mocked_execute): def test_delete_instances_less_than_batch_size(mocked_execute): """Test that delete_instances works as intended when instance count is less than batch size.""" - instances = ['instance-%d' % i for i in range(5)] + instances = [f'instance-{i}' for i in range(5)] mocked_execute.return_value = new_process.ProcessResult(0, '', False) zone = 'us-central1-a' expected_command = (['gcloud', 'compute', 'instances', 'delete', '-q'] + @@ -124,16 +124,16 @@ def test_delete_instances_less_than_batch_size(mocked_execute): def test_delete_instances_greater_than_batch_size(mocked_execute): """Test that delete_instances works as intended when instance count is more than batch size.""" - instances = ['instance-%d' % i for i in range(103)] + instances = [f'instance-{i}' for i in range(103)] mocked_execute.return_value = new_process.ProcessResult(0, '', False) zone = 'us-central1-a' result = gcloud.delete_instances(instances, zone) assert result expected_command_1 = (['gcloud', 'compute', 'instances', 'delete', '-q'] + - ['instance-%d' % i for i in range(100)] + + [f'instance-{i}' for i in range(100)] + ['--zone', zone]) expected_command_2 = (['gcloud', 'compute', 'instances', 'delete', '-q'] + - ['instance-%d' % i for i in range(100, 103)] + + [f'instance-{i}' for i in range(100, 103)] + ['--zone', zone]) mocked_execute.assert_has_calls([ mock.call(expected_command_1, expect_zero=False), @@ -144,7 +144,7 @@ def test_delete_instances_greater_than_batch_size(mocked_execute): @mock.patch('common.new_process.execute') def test_delete_instances_fail(mocked_execute): """Test that delete_instances returns False when instance deletion fails.""" - instances = ['instance-%d' % i for i in range(5)] + instances = [f'instance-{i}' for i in range(5)] mocked_execute.return_value = new_process.ProcessResult(1, 'Error', False) zone = 'us-central1-a' expected_command = (['gcloud', 'compute', 'instances', 'delete', '-q'] + @@ -175,9 +175,8 @@ def test_create_instance_template(mocked_execute): ] mocked_execute.assert_called_with(expected_command) expected_result = ( - 'https://www.googleapis.com/compute/v1/projects/{project}' - '/global/instanceTemplates/{name}').format(project=project, - name=template_name) + f'https://www.googleapis.com/compute/v1/projects/{project}' + f'/global/instanceTemplates/{template_name}') assert result == expected_result diff --git a/common/test_local_filestore.py b/common/test_local_filestore.py index 6781adfd5..c08869180 100644 --- a/common/test_local_filestore.py +++ b/common/test_local_filestore.py @@ -26,7 +26,7 @@ def test_rm(tmp_path): """Tests rm works as expected.""" file_path = tmp_path / 'file' data = 'hello' - with open(file_path, 'w') as file_handle: + with open(file_path, 'w', encoding='utf-8') as file_handle: file_handle.write(data) local_filestore.rm(str(file_path)) assert not os.path.exists(file_path) @@ -52,9 +52,9 @@ def test_ls_one_file_per_line(tmp_path): dir_path = tmp_path file1 = dir_path / 'file1' file2 = dir_path / 'file2' - with open(file1, 'w+'): + with open(file1, 'w+', encoding='utf-8'): pass - with open(file2, 'w+'): + with open(file2, 'w+', encoding='utf-8'): pass assert local_filestore.ls(str(dir_path)).output == 'file1\nfile2\n' @@ -63,11 +63,11 @@ def test_cp(tmp_path): """Tests cp works as expected.""" source = tmp_path / 'source' data = 'hello' - with open(source, 'w') as file_handle: + with open(source, 'w', encoding='utf-8') as file_handle: file_handle.write(data) destination = tmp_path / 'destination' local_filestore.cp(str(source), str(destination)) - with open(destination) as file_handle: + with open(destination, encoding='utf-8') as file_handle: assert file_handle.read() == data @@ -77,7 +77,7 @@ def test_cp_nonexistent_dest(tmp_path): source_dir.mkdir() source_file = source_dir / 'file1' cp_dest_dir = tmp_path / 'cp_test' / 'intermediate' / 'cp_dest' - with open(source_file, 'w'): + with open(source_file, 'w', encoding='utf-8'): pass # Should run without exceptions. @@ -90,7 +90,7 @@ def test_rsync_nonexistent_dest(tmp_path): source_dir.mkdir() source_file = source_dir / 'file1' rsync_dest_dir = tmp_path / 'rsync_test' / 'intermediate' / 'rsync_dest' - with open(source_file, 'w'): + with open(source_file, 'w', encoding='utf-8'): pass # Should run without exceptions. diff --git a/common/test_new_process.py b/common/test_new_process.py index c1f596d7f..e527dcd16 100644 --- a/common/test_new_process.py +++ b/common/test_new_process.py @@ -53,11 +53,11 @@ def test_timeout(self): def test_output_file(self, mocked_info, tmp_path): """Test that execute handles the output_file argument as intended.""" output_file_path = tmp_path / 'output' - with open(output_file_path, 'w') as output_file: + with open(output_file_path, 'w', encoding='utf-8') as output_file: new_process.execute(self.COMMAND, timeout=1, output_file=output_file, expect_zero=False) - with open(output_file_path, 'r') as output_file: + with open(output_file_path, 'r', encoding='utf-8') as output_file: assert output_file.read() == 'Hello, World!\n' diff --git a/common/utils.py b/common/utils.py index 3b508f23f..066aeac70 100644 --- a/common/utils.py +++ b/common/utils.py @@ -47,7 +47,8 @@ def is_local(): return _is_local try: # TODO(github.com/google/fuzzbench/issues/82): Get rid of this. - urllib.request.urlopen('http://metadata.google.internal') + with urllib.request.urlopen('http://metadata.google.internal'): + pass _is_local = False except urllib.error.URLError: _is_local = True diff --git a/common/yaml_utils.py b/common/yaml_utils.py index 54191ec8b..6b003d0f0 100644 --- a/common/yaml_utils.py +++ b/common/yaml_utils.py @@ -17,11 +17,11 @@ def read(yaml_filename): """Reads and loads yaml file specified by |yaml_filename|.""" - with open(yaml_filename) as file_handle: + with open(yaml_filename, encoding='utf-8') as file_handle: return yaml.load(file_handle, yaml.SafeLoader) def write(yaml_filename, data): """Writes |data| to a new yaml file at |yaml_filename|.""" - with open(yaml_filename, 'w') as file_handle: + with open(yaml_filename, 'w', encoding='utf-8') as file_handle: return yaml.dump(data, file_handle) diff --git a/config/experiment.yaml b/config/experiment.yaml new file mode 100644 index 000000000..4f48d8196 --- /dev/null +++ b/config/experiment.yaml @@ -0,0 +1,52 @@ +benchmarks: +- curl_curl_fuzzer_http +- freetype2-2017 +- harfbuzz-1.3.2 +- jsoncpp_jsoncpp_fuzzer +- lcms-2017-03-21 +- libjpeg-turbo-07-2017 +- libpcap_fuzz_both +- libpng-1.2.56 +- libxml2-v2.9.2 +- libxslt_xpath +- mbedtls_fuzz_dtlsclient +- openssl_x509 +- openthread-2019-12-23 +- php_php-fuzz-parser +- proj4-2017-08-14 +- re2-2014-12-09 +- sqlite3_ossfuzz +- systemd_fuzz-link-parser +- vorbis-2017-12-11 +- woff2-2016-05-06 +- zlib_zlib_uncompress_fuzzer +cloud_compute_zone: us-central1-b +cloud_project: fuzzbench +cloud_sql_instance_connection_name: fuzzbench:us-central1:postgres-experiment-db=tcp:5432 +concurrent_builds: 30 +custom_seed_corpus_dir: null +description: null +docker_registry: gcr.io/fuzzbench +experiment: 2022-11-22-02-33-15m-dongge +experiment_filestore: gs://fuzzbench-data +fuzzers: +- libfuzzer +git_hash: c0608813379f2dce012fcb59d2dd12f581f8bc30 +local_experiment: false +max_total_time: 910 +measurers_cpus: null +merge_with_nonprivate: true +no_dictionaries: false +no_seeds: false +oss_fuzz_corpus: false +preemptible_runners: true +private: false +region_coverage: false +report_filestore: gs://www.fuzzbench.com/reports +runner_machine_type: n1-standard-1 +runner_memory: 12GB +runner_num_cpu_cores: 1 +runners_cpus: null +snapshot_period: 900 +trials: 2 +worker_pool_name: projects/fuzzbench/locations/us-central1/workerPools/buildpool diff --git a/database/utils.py b/database/utils.py index cde16f1f2..f1f6ec37c 100644 --- a/database/utils.py +++ b/database/utils.py @@ -34,8 +34,8 @@ def initialize(): postgres_password = os.getenv('POSTGRES_PASSWORD') assert postgres_password, 'POSTGRES_PASSWORD needs to be set.' database_url = ( - 'postgresql+psycopg2://postgres:{password}@127.0.0.1:5432'.format( - password=postgres_password)) + f'postgresql+psycopg2://postgres:{postgres_password}@127.0.0.1:5432' + ) global engine engine = sqlalchemy.create_engine(database_url) @@ -67,6 +67,7 @@ def cleanup(): @contextmanager def session_scope(): """Provide a transactional scope around a series of operations.""" + # pylint: disable=global-variable-not-assigned global session global engine global lock diff --git a/docker/generate_makefile.py b/docker/generate_makefile.py index 8b17232ab..fff26cd92 100755 --- a/docker/generate_makefile.py +++ b/docker/generate_makefile.py @@ -21,7 +21,7 @@ from common import fuzzer_utils from experiment.build import docker_images -BASE_TAG = "gcr.io/fuzzbench" +BASE_TAG = 'gcr.io/fuzzbench' BENCHMARK_DIR = benchmark_utils.BENCHMARKS_DIR @@ -144,7 +144,7 @@ def main(): return 1 makefile_path = sys.argv[1] makefile_contents = generate_makefile() - with open(makefile_path, 'w') as file_handle: + with open(makefile_path, 'w', encoding='utf-8') as file_handle: file_handle.write(makefile_contents) return 0 diff --git a/experiment/build/build_utils.py b/experiment/build/build_utils.py index 47fcbc92f..94f1c1b7e 100644 --- a/experiment/build/build_utils.py +++ b/experiment/build/build_utils.py @@ -21,8 +21,8 @@ def store_build_logs(build_config, build_result): """Save build results in the build logs bucket.""" - build_output = ('Command returned {retcode}.\nOutput: {output}'.format( - retcode=build_result.retcode, output=build_result.output)) + build_output = (f'Command returned {build_result.retcode}.\n' + f'Output: {build_result.output}') with tempfile.NamedTemporaryFile(mode='w') as tmp: tmp.write(build_output) tmp.flush() diff --git a/experiment/build/gcb_build.py b/experiment/build/gcb_build.py index 5d0c7c008..1401876c7 100644 --- a/experiment/build/gcb_build.py +++ b/experiment/build/gcb_build.py @@ -64,7 +64,7 @@ def build_coverage(benchmark): config = generate_cloudbuild.create_cloudbuild_spec(image_templates, benchmark=benchmark, fuzzer='coverage') - config_name = 'benchmark-{benchmark}-coverage'.format(benchmark=benchmark) + config_name = f'benchmark-{benchmark}-coverage' _build(config, config_name) @@ -75,11 +75,12 @@ def _build( """Submit build to GCB.""" with tempfile.NamedTemporaryFile() as config_file: yaml_utils.write(config_file.name, config) - logger.debug('Using build configuration: %s' % config) + logger.debug('Using build configuration: %s', config) + + config_arg = f'--config={config_file.name}' - config_arg = '--config=%s' % config_file.name # Use "s" suffix to denote seconds. - timeout_arg = '--timeout=%ds' % timeout_seconds + timeout_arg = f'--timeout={timeout_seconds}s' command = [ 'gcloud', @@ -118,8 +119,7 @@ def build_fuzzer_benchmark(fuzzer: str, benchmark: str): if image_specs['type'] in ('base', 'coverage', 'dispatcher'): continue image_templates[image_name] = image_specs - config_name = 'benchmark-{benchmark}-fuzzer-{fuzzer}'.format( - benchmark=benchmark, fuzzer=fuzzer) + config_name = f'benchmark-{benchmark}-fuzzer-{fuzzer}' config = generate_cloudbuild.create_cloudbuild_spec(image_templates, benchmark=benchmark, fuzzer=fuzzer) diff --git a/experiment/build/local_build.py b/experiment/build/local_build.py index 1f6fd386c..532f1e9f3 100644 --- a/experiment/build/local_build.py +++ b/experiment/build/local_build.py @@ -54,7 +54,7 @@ def make_shared_coverage_binaries_dir(): def build_coverage(benchmark): """Build (locally) coverage image for benchmark.""" - image_name = 'build-coverage-{}'.format(benchmark) + image_name = f'build-coverage-{benchmark}' result = make([image_name]) if result.retcode: return result @@ -66,14 +66,15 @@ def build_coverage(benchmark): def copy_coverage_binaries(benchmark): """Copy coverage binaries in a local experiment.""" shared_coverage_binaries_dir = get_shared_coverage_binaries_dir() - mount_arg = '{0}:{0}'.format(shared_coverage_binaries_dir) + mount_arg = f'{shared_coverage_binaries_dir}:{shared_coverage_binaries_dir}' builder_image_url = benchmark_utils.get_builder_image_url( benchmark, 'coverage', environment.get('DOCKER_REGISTRY')) - coverage_build_archive = 'coverage-build-{}.tar.gz'.format(benchmark) + coverage_build_archive = f'coverage-build-{benchmark}.tar.gz' coverage_build_archive_shared_dir_path = os.path.join( shared_coverage_binaries_dir, coverage_build_archive) - command = 'cd /out; tar -czvf {} * /src /work'.format( - coverage_build_archive_shared_dir_path) + command = ( + '(cd /out; ' + f'tar -czvf {coverage_build_archive_shared_dir_path} * /src /work)') return new_process.execute([ 'docker', 'run', '-v', mount_arg, builder_image_url, '/bin/bash', '-c', command @@ -82,5 +83,5 @@ def copy_coverage_binaries(benchmark): def build_fuzzer_benchmark(fuzzer: str, benchmark: str) -> bool: """Builds |benchmark| for |fuzzer|.""" - image_name = 'build-{}-{}'.format(fuzzer, benchmark) + image_name = f'build-{fuzzer}-{benchmark}' make([image_name]) diff --git a/experiment/build/test_builder.py b/experiment/build/test_builder.py index acaa1014d..3df44a255 100644 --- a/experiment/build/test_builder.py +++ b/experiment/build/test_builder.py @@ -91,7 +91,6 @@ def builder_integration(experiment): yield -# pylint: disable=no-self-use @pytest.mark.skipif( not os.getenv('TEST_INTEGRATION_ALL'), reason='Tests take too long and can interfere with real ' diff --git a/experiment/conftest.py b/experiment/conftest.py index 8e517ca89..e7ef5a402 100644 --- a/experiment/conftest.py +++ b/experiment/conftest.py @@ -25,7 +25,7 @@ def experiment_config(): config_filepath = os.path.join(os.path.dirname(__file__), 'test_data', 'experiment-config.yaml') - with open(config_filepath) as file_handle: + with open(config_filepath, encoding='utf-8') as file_handle: return yaml.load(file_handle, yaml.SafeLoader) @@ -36,5 +36,5 @@ def local_experiment_config(): config_filepath = os.path.join(os.path.dirname(__file__), 'test_data', 'local-experiment-config.yaml') - with open(config_filepath) as file_handle: + with open(config_filepath, encoding='utf-8') as file_handle: return yaml.load(file_handle, yaml.SafeLoader) diff --git a/experiment/measurer/coverage_utils.py b/experiment/measurer/coverage_utils.py index 7123ed4e2..20333b2d3 100644 --- a/experiment/measurer/coverage_utils.py +++ b/experiment/measurer/coverage_utils.py @@ -60,10 +60,8 @@ def generate_coverage_reports(experiment_config: dict): def generate_coverage_report(experiment, benchmark, fuzzer, region_coverage): """Generates the coverage report for one pair of benchmark and fuzzer.""" - logger.info( - ('Generating coverage report for ' - 'benchmark: {benchmark} fuzzer: {fuzzer}.').format(benchmark=benchmark, - fuzzer=fuzzer)) + logger.info('Generating coverage report for benchmark: %s fuzzer: %s.', + benchmark, fuzzer) try: coverage_reporter = CoverageReporter(experiment, fuzzer, benchmark, @@ -122,9 +120,8 @@ def __init__(self, experiment, fuzzer, benchmark, region_coverage): def merge_profdata_files(self): """Merge profdata files from |src_files| to |dst_files|.""" - logger.info('Merging profdata for fuzzer: ' - '{fuzzer},benchmark: {benchmark}.'.format( - fuzzer=self.fuzzer, benchmark=self.benchmark)) + logger.info('Merging profdata for fuzzer: %s, benchmark: %s.', + self.fuzzer, self.benchmark) files_to_merge = [] for trial_id in self.trial_ids: @@ -148,24 +145,27 @@ def generate_coverage_summary_json(self): if result.retcode != 0: logger.error( 'Merged coverage summary json file generation failed for ' - 'fuzzer: {fuzzer},benchmark: {benchmark}.'.format( - fuzzer=self.fuzzer, benchmark=self.benchmark)) + f'fuzzer: {self.fuzzer},benchmark: {self.benchmark}.') def generate_coverage_report(self): """Generates the coverage report and stores in bucket.""" command = [ - 'llvm-cov', 'show', '-format=html', - '-path-equivalence=/,{prefix}'.format(prefix=self.source_files_dir), - '-output-dir={dst_dir}'.format(dst_dir=self.report_dir), - '-Xdemangler', 'c++filt', '-Xdemangler', '-n', self.binary_file, - '-instr-profile={profdata}'.format( - profdata=self.merged_profdata_file) + 'llvm-cov', + 'show', + '-format=html', + f'-path-equivalence=/,{self.source_files_dir}', + f'-output-dir={self.report_dir}', + '-Xdemangler', + 'c++filt', + '-Xdemangler', + '-n', + self.binary_file, + f'-instr-profile={self.merged_profdata_file}', ] result = new_process.execute(command, expect_zero=False) if result.retcode != 0: logger.error('Coverage report generation failed for ' - 'fuzzer: {fuzzer},benchmark: {benchmark}.'.format( - fuzzer=self.fuzzer, benchmark=self.benchmark)) + f'fuzzer: {self.fuzzer},benchmark: {self.benchmark}.') return src_dir = self.report_dir @@ -183,7 +183,7 @@ def generate_coverage_branches_json(self): coverage_json_src = os.path.join(self.data_dir, 'covered_branches.json') coverage_json_dst = exp_path.filestore(coverage_json_src) filesystem.create_directory(self.data_dir) - with open(coverage_json_src, 'w') as file_handle: + with open(coverage_json_src, 'w', encoding='utf-8') as file_handle: json.dump(edges_covered, file_handle) filestore_utils.cp(coverage_json_src, coverage_json_dst, @@ -192,12 +192,12 @@ def generate_coverage_branches_json(self): def get_coverage_archive_name(benchmark): """Gets the archive name for |benchmark|.""" - return 'coverage-build-%s.tar.gz' % benchmark + return f'coverage-build-{benchmark}.tar.gz' def get_profdata_file_name(trial_id): """Returns the profdata file name for |trial_id|.""" - return 'data-{id}.profdata'.format(id=trial_id) + return f'data-{trial_id}.profdata' def get_coverage_binary(benchmark: str) -> str: @@ -234,7 +234,7 @@ def merge_profdata_files(src_files, dst_file): def get_coverage_infomation(coverage_summary_file): """Reads the coverage information from |coverage_summary_file| and skip possible warnings in the file.""" - with open(coverage_summary_file) as summary: + with open(coverage_summary_file, encoding='utf-8') as summary: return json.loads(summary.readlines()[-1]) @@ -264,15 +264,20 @@ def generate_json_summary(coverage_binary, """Generates the json summary file from |coverage_binary| and |profdata_file|.""" command = [ - 'llvm-cov', 'export', '-format=text', '-num-threads=1', - '-region-coverage-gt=0', '-skip-expansions', coverage_binary, - '-instr-profile=%s' % profdata_file + 'llvm-cov', + 'export', + '-format=text', + '-num-threads=1', + '-region-coverage-gt=0', + '-skip-expansions', + coverage_binary, + f'-instr-profile={profdata_file}', ] if summary_only: command.append('-summary-only') - with open(output_file, 'w') as dst_file: + with open(output_file, 'w', encoding='utf-8') as dst_file: result = new_process.execute(command, output_file=dst_file, expect_zero=False) diff --git a/experiment/measurer/measure_manager.py b/experiment/measurer/measure_manager.py index b926c2515..bfca81a6e 100644 --- a/experiment/measurer/measure_manager.py +++ b/experiment/measurer/measure_manager.py @@ -48,7 +48,7 @@ from experiment.measurer import run_crashes from experiment import scheduler -logger = logs.Logger('measurer') # pylint: disable=invalid-name +logger = logs.Logger('measurer') SnapshotMeasureRequest = collections.namedtuple( 'SnapshotMeasureRequest', ['fuzzer', 'benchmark', 'trial_id', 'cycle']) @@ -109,8 +109,8 @@ def measure_loop(experiment: str, local_experiment = experiment_utils.is_local_experiment() if local_experiment: cores_queue = multiprocessing.Queue() - logger.info('Scheduling measurers from core %d to %d.' % - (runners_cpus, runners_cpus + measurers_cpus - 1)) + logger.info('Scheduling measurers from core %d to %d.', + runners_cpus, runners_cpus + measurers_cpus - 1) for cpu in range(runners_cpus, runners_cpus + measurers_cpus): cores_queue.put(cpu) pool_args = (measurers_cpus, _process_init, (cores_queue,)) @@ -122,14 +122,16 @@ def measure_loop(experiment: str, set_up_coverage_binaries(pool, experiment) # Using Multiprocessing.Queue will fail with a complaint about # inheriting queue. - q = manager.Queue() # pytype: disable=attribute-error + # pytype: disable=attribute-error + multiprocessing_queue = manager.Queue() while True: try: # Get whether all trials have ended before we measure to prevent # races. all_trials_ended = scheduler.all_trials_ended(experiment) - if not measure_all_trials(experiment, max_total_time, pool, q, + if not measure_all_trials(experiment, max_total_time, pool, + multiprocessing_queue, region_coverage): # We didn't measure any trials. if all_trials_ended: @@ -145,8 +147,8 @@ def measure_loop(experiment: str, logger.info('Finished measure loop.') -def measure_all_trials(experiment: str, max_total_time: int, pool, q, - region_coverage) -> bool: # pylint: disable=invalid-name +def measure_all_trials(experiment: str, max_total_time: int, pool, + multiprocessing_queue, region_coverage) -> bool: """Get coverage data (with coverage runs) for all active trials. Note that this should not be called unless multiprocessing.set_start_method('spawn') was called first. Otherwise it will use fork which breaks logging.""" @@ -163,7 +165,7 @@ def measure_all_trials(experiment: str, max_total_time: int, pool, q, return False measure_trial_coverage_args = [ - (unmeasured_snapshot, max_cycle, q, region_coverage) + (unmeasured_snapshot, max_cycle, multiprocessing_queue, region_coverage) for unmeasured_snapshot in unmeasured_snapshots ] @@ -189,7 +191,8 @@ def save_snapshots(): while True: try: - snapshot = q.get(timeout=SNAPSHOT_QUEUE_GET_TIMEOUT) + snapshot = multiprocessing_queue.get( + timeout=SNAPSHOT_QUEUE_GET_TIMEOUT) snapshots.append(snapshot) except queue.Empty: if result.ready(): @@ -329,30 +332,31 @@ def extract_corpus(corpus_archive: str, sha_blacklist: Set[str], output_directory: str): """Extract a corpus from |corpus_archive| to |output_directory|.""" pathlib.Path(output_directory).mkdir(exist_ok=True) - tar = tarfile.open(corpus_archive, 'r:gz') - for member in tar.getmembers(): + with tarfile.open(corpus_archive, 'r:gz') as tar: + for member in tar.getmembers(): - if not member.isfile(): - # We don't care about directory structure. So skip if not a file. - continue + if not member.isfile(): + # We don't care about directory structure. + # So skip if not a file. + continue - member_file_handle = tar.extractfile(member) - if not member_file_handle: - logger.info('Failed to get handle to %s', member) - continue + member_file_handle = tar.extractfile(member) + if not member_file_handle: + logger.info('Failed to get handle to %s.', member) + continue - member_contents = member_file_handle.read() - filename = utils.string_hash(member_contents) - if filename in sha_blacklist: - continue + member_contents = member_file_handle.read() + filename = utils.string_hash(member_contents) + if filename in sha_blacklist: + continue - file_path = os.path.join(output_directory, filename) + file_path = os.path.join(output_directory, filename) - if os.path.exists(file_path): - # Don't write out duplicates in the archive. - continue + if os.path.exists(file_path): + # Don't write out duplicates in the archive. + continue - filesystem.write(file_path, member_contents, 'wb') + filesystem.write(file_path, member_contents, 'wb') class SnapshotMeasurer(coverage_utils.TrialCoverage): # pylint: disable=too-many-instance-attributes @@ -572,8 +576,7 @@ def process_crashes(self, cycle): crash_metadata = run_crashes.do_crashes_run(app_binary, self.crashes_dir) crashes = [] - for crash_key in crash_metadata: - crash = crash_metadata[crash_key] + for crash_key, crash in crash_metadata.items(): crashes.append( models.Crash(crash_key=crash_key, crash_testcase=crash.crash_testcase, @@ -623,9 +626,9 @@ def get_fuzzer_stats(stats_filestore_path): return json.loads(stats_str) -def measure_trial_coverage( # pylint: disable=invalid-name - measure_req, max_cycle: int, q: multiprocessing.Queue, - region_coverage) -> models.Snapshot: +def measure_trial_coverage(measure_req, max_cycle: int, + multiprocessing_queue: multiprocessing.Queue, + region_coverage) -> models.Snapshot: """Measure the coverage obtained by |trial_num| on |benchmark| using |fuzzer|.""" initialize_logs() @@ -640,7 +643,7 @@ def measure_trial_coverage( # pylint: disable=invalid-name region_coverage) if not snapshot: break - q.put(snapshot) + multiprocessing_queue.put(snapshot) except Exception: # pylint: disable=broad-except logger.error('Error measuring cycle.', extras={ @@ -748,15 +751,15 @@ def set_up_coverage_binary(benchmark): coverage_binaries_dir = build_utils.get_coverage_binaries_dir() benchmark_coverage_binary_dir = coverage_binaries_dir / benchmark filesystem.create_directory(benchmark_coverage_binary_dir) - archive_name = 'coverage-build-%s.tar.gz' % benchmark + archive_name = f'coverage-build-{benchmark}.tar.gz' archive_filestore_path = exp_path.filestore(coverage_binaries_dir / archive_name) filestore_utils.cp(archive_filestore_path, str(benchmark_coverage_binary_dir)) archive_path = benchmark_coverage_binary_dir / archive_name - tar = tarfile.open(archive_path, 'r:gz') - tar.extractall(benchmark_coverage_binary_dir) - os.remove(archive_path) + with tarfile.open(archive_path, 'r:gz') as tar: + tar.extractall(benchmark_coverage_binary_dir) + os.remove(archive_path) def initialize_logs(): diff --git a/experiment/measurer/run_coverage.py b/experiment/measurer/run_coverage.py index 4f83d8a94..d031efe96 100644 --- a/experiment/measurer/run_coverage.py +++ b/experiment/measurer/run_coverage.py @@ -57,10 +57,9 @@ def do_coverage_run( # pylint: disable=too-many-locals with tempfile.TemporaryDirectory() as merge_dir: command = [ coverage_binary, '-merge=1', '-dump_coverage=1', - '-artifact_prefix=%s/' % crashes_dir, - '-timeout=%d' % UNIT_TIMEOUT, - '-rss_limit_mb=%d' % RSS_LIMIT_MB, - '-max_total_time=%d' % (MAX_TOTAL_TIME - EXIT_BUFFER), merge_dir, + f'-artifact_prefix={crashes_dir}/', f'-timeout={UNIT_TIMEOUT}', + f'-rss_limit_mb={RSS_LIMIT_MB}', + f'-max_total_time={MAX_TOTAL_TIME - EXIT_BUFFER}', merge_dir, new_units_dir ] coverage_binary_dir = os.path.dirname(coverage_binary) diff --git a/experiment/measurer/run_crashes.py b/experiment/measurer/run_crashes.py index f63daa8a1..4816efd7e 100644 --- a/experiment/measurer/run_crashes.py +++ b/experiment/measurer/run_crashes.py @@ -59,9 +59,8 @@ def process_crash(app_binary, crash_testcase_path, crashes_dir): env = os.environ.copy() sanitizer.set_sanitizer_options(env) command = [ - app_binary, - '-timeout=%d' % run_coverage.UNIT_TIMEOUT, - '-rss_limit_mb=%d' % run_coverage.RSS_LIMIT_MB, crash_testcase_path + app_binary, f'-timeout={run_coverage.UNIT_TIMEOUT}', + f'-rss_limit_mb={run_coverage.RSS_LIMIT_MB}', crash_testcase_path ] app_binary_dir = os.path.dirname(app_binary) result = new_process.execute(command, diff --git a/experiment/measurer/test_measure_manager.py b/experiment/measurer/test_measure_manager.py index 29be769b5..26b0ecc2e 100644 --- a/experiment/measurer/test_measure_manager.py +++ b/experiment/measurer/test_measure_manager.py @@ -146,8 +146,8 @@ def test_generate_summary(mocked_get_coverage_binary, mocked_execute, snapshot_measurer = measure_manager.SnapshotMeasurer( FUZZER, BENCHMARK, TRIAL_NUM, SNAPSHOT_LOGGER, REGION_COVERAGE) - snapshot_measurer.cov_summary_file = "/reports/cov_summary.txt" - snapshot_measurer.profdata_file = "/reports/data.profdata" + snapshot_measurer.cov_summary_file = '/reports/cov_summary.txt' + snapshot_measurer.profdata_file = '/reports/data.profdata' fs.create_dir('/reports') fs.create_file(snapshot_measurer.profdata_file, contents='fake_contents') snapshot_measurer.generate_summary(CYCLE) @@ -162,7 +162,7 @@ def test_generate_summary(mocked_get_coverage_binary, mocked_execute, assert (len(mocked_execute.call_args_list)) == 1 args = mocked_execute.call_args_list[0] assert args[0][0] == expected - assert args[1]['output_file'].name == "/reports/cov_summary.txt" + assert args[1]['output_file'].name == '/reports/cov_summary.txt' @mock.patch('common.logs.error') @@ -199,7 +199,7 @@ def test_measure_all_trials_not_ready(mocked_rsync, mocked_ls, experiment): @mock.patch('multiprocessing.pool.ThreadPool', test_utils.MockPool) @mock.patch('common.new_process.execute') @mock.patch('common.filesystem.directories_have_same_files') -@pytest.mark.skip(reason="See crbug.com/1012329") +@pytest.mark.skip(reason='See crbug.com/1012329') def test_measure_all_trials_no_more(mocked_directories_have_same_files, mocked_execute): """Test measure_all_trials does what is intended when the experiment is @@ -359,9 +359,6 @@ def get_test_data_path(*subpaths): return os.path.join(TEST_DATA_PATH, *subpaths) -# pylint: disable=no-self-use - - class TestIntegrationMeasurement: """Integration tests for measurement.""" @@ -409,7 +406,7 @@ def test_measure_snapshot_coverage( # pylint: disable=too-many-locals # Set up the snapshot archive. cycle = 1 archive = get_test_data_path('test_measure_snapshot_coverage', - 'corpus-archive-%04d.tar.gz' % cycle) + f'corpus-archive-{cycle:04d}.tar.gz') corpus_dir = os.path.join(snapshot_measurer.trial_dir, 'corpus') os.makedirs(corpus_dir) shutil.copy(archive, corpus_dir) diff --git a/experiment/measurer/test_run_coverage.py b/experiment/measurer/test_run_coverage.py index 8477caca6..9a5b000b9 100644 --- a/experiment/measurer/test_run_coverage.py +++ b/experiment/measurer/test_run_coverage.py @@ -13,8 +13,6 @@ # limitations under the License. """Tests for run_crashes.py.""" -# pylint: disable=no-self-use - import os from unittest import mock import glob diff --git a/experiment/measurer/test_run_crashes.py b/experiment/measurer/test_run_crashes.py index 3c26a2776..f967a864a 100644 --- a/experiment/measurer/test_run_crashes.py +++ b/experiment/measurer/test_run_crashes.py @@ -13,8 +13,6 @@ # limitations under the License. """Tests for run_coverage.py.""" -# pylint: disable=no-self-use - import os import pytest @@ -36,7 +34,7 @@ def test_integration_do_coverage_run_crash(self): """Test that do_coverage_run returns crashing inputs.""" llvm_tools_path = os.path.abspath( os.path.join(TEST_DATA_PATH, '..', 'llvm_tools')) - os.environ["PATH"] = llvm_tools_path + os.pathsep + os.environ["PATH"] + os.environ['PATH'] = llvm_tools_path + os.pathsep + os.environ['PATH'] crashes_dir = os.path.join(TEST_DATA_PATH, 'crash-corpus') crashes = run_crashes.do_crashes_run(self.APP_BINARY_PATH, crashes_dir) diff --git a/experiment/run_experiment.py b/experiment/run_experiment.py index 85e5a2e38..ef6fcd5bd 100644 --- a/experiment/run_experiment.py +++ b/experiment/run_experiment.py @@ -189,7 +189,7 @@ def read_and_validate_experiment_config(config_filename: str) -> Dict: all_params_valid = _validate_config_parameters(config, config_requirements) all_values_valid = _validate_config_values(config, config_requirements) if not all_params_valid or not all_values_valid: - raise ValidationError('Config: %s is invalid.' % config_filename) + raise ValidationError(f'Config: {config_filename} is invalid.') _set_default_config_values(config, local_experiment) return config @@ -211,20 +211,20 @@ def get_directories(parent_dir): def validate_custom_seed_corpus(custom_seed_corpus_dir, benchmarks): """Validate seed corpus provided by user""" if not os.path.isdir(custom_seed_corpus_dir): - raise ValidationError('Corpus location "%s" is invalid.' % - custom_seed_corpus_dir) + raise ValidationError( + f'Corpus location "{custom_seed_corpus_dir}" is invalid.') for benchmark in benchmarks: benchmark_corpus_dir = os.path.join(custom_seed_corpus_dir, benchmark) if not os.path.exists(benchmark_corpus_dir): raise ValidationError('Custom seed corpus directory for ' - 'benchmark "%s" does not exist.' % benchmark) + f'benchmark "{benchmark}" does not exist.') if not os.path.isdir(benchmark_corpus_dir): - raise ValidationError('Seed corpus of benchmark "%s" must be ' - 'a directory.' % benchmark) + raise ValidationError( + f'Seed corpus of benchmark "{benchmark}" must be a directory.') if not os.listdir(benchmark_corpus_dir): - raise ValidationError('Seed corpus of benchmark "%s" is empty.' % - benchmark) + raise ValidationError( + f'Seed corpus of benchmark "{benchmark}" is empty.') def validate_benchmarks(benchmarks: List[str]): @@ -232,13 +232,13 @@ def validate_benchmarks(benchmarks: List[str]): benchmark_types = set() for benchmark in set(benchmarks): if benchmarks.count(benchmark) > 1: - raise ValidationError('Benchmark "%s" is included more than once.' % - benchmark) + raise ValidationError( + f'Benchmark "{benchmark}" is included more than once.') # Validate benchmarks here. It's possible someone might run an # experiment without going through presubmit. Better to catch an invalid # benchmark than see it in production. if not benchmark_utils.validate(benchmark): - raise ValidationError('Benchmark "%s" is invalid.' % benchmark) + raise ValidationError(f'Benchmark "{benchmark}" is invalid.') benchmark_types.add(benchmark_utils.get_type(benchmark)) @@ -251,7 +251,7 @@ def validate_benchmarks(benchmarks: List[str]): def validate_fuzzer(fuzzer: str): """Parses and validates a fuzzer name.""" if not fuzzer_utils.validate(fuzzer): - raise ValidationError('Fuzzer: %s is invalid.' % fuzzer) + raise ValidationError(f'Fuzzer: {fuzzer} is invalid.') def validate_experiment_name(experiment_name: str): @@ -259,8 +259,8 @@ def validate_experiment_name(experiment_name: str): instances.""" if not re.match(EXPERIMENT_CONFIG_REGEX, experiment_name): raise ValidationError( - 'Experiment name "%s" is invalid. Must match: "%s"' % - (experiment_name, EXPERIMENT_CONFIG_REGEX.pattern)) + f'Experiment name "{experiment_name}" is invalid. ' + f'Must match: "{EXPERIMENT_CONFIG_REGEX.pattern}"') def set_up_experiment_config_file(config): @@ -269,7 +269,8 @@ def set_up_experiment_config_file(config): filesystem.recreate_directory(experiment_utils.CONFIG_DIR) experiment_config_filename = ( experiment_utils.get_internal_experiment_config_relative_path()) - with open(experiment_config_filename, 'w') as experiment_config_file: + with open(experiment_config_filename, 'w', + encoding='utf-8') as experiment_config_file: yaml.dump(config, experiment_config_file, default_flow_style=False) @@ -371,7 +372,7 @@ def add_oss_fuzz_corpus(benchmark, oss_fuzz_corpora_dir): fuzz_target = benchmark_utils.get_fuzz_target(benchmark) if not fuzz_target.startswith(project): - full_fuzz_target = '%s_%s' % (project, fuzz_target) + full_fuzz_target = f'{project}_{fuzz_target}' else: full_fuzz_target = fuzz_target @@ -456,35 +457,31 @@ def start(self): self.config['experiment_filestore']) filesystem.create_directory(experiment_filestore_path) sql_database_arg = ( - 'SQL_DATABASE_URL=sqlite:///{}?check_same_thread=False'.format( - os.path.join(experiment_filestore_path, 'local.db'))) + 'SQL_DATABASE_URL=sqlite:///' + f'{os.path.join(experiment_filestore_path, "local.db")}' + '?check_same_thread=False') docker_registry = self.config['docker_registry'] - set_instance_name_arg = 'INSTANCE_NAME={instance_name}'.format( - instance_name=self.instance_name) - set_experiment_arg = 'EXPERIMENT={experiment}'.format( - experiment=self.config['experiment']) - shared_experiment_filestore_arg = '{0}:{0}'.format( - self.config['experiment_filestore']) + set_instance_name_arg = f'INSTANCE_NAME={self.instance_name}' + set_experiment_arg = f'EXPERIMENT={self.config["experiment"]}' + filestore = self.config['experiment_filestore'] + shared_experiment_filestore_arg = f'{filestore}:{filestore}' # TODO: (#484) Use config in function args or set as environment # variables. - set_docker_registry_arg = 'DOCKER_REGISTRY={}'.format(docker_registry) + set_docker_registry_arg = f'DOCKER_REGISTRY={docker_registry}' set_experiment_filestore_arg = ( - 'EXPERIMENT_FILESTORE={experiment_filestore}'.format( - experiment_filestore=self.config['experiment_filestore'])) - shared_report_filestore_arg = '{0}:{0}'.format( - self.config['report_filestore']) - set_report_filestore_arg = ( - 'REPORT_FILESTORE={report_filestore}'.format( - report_filestore=self.config['report_filestore'])) - set_snapshot_period_arg = 'SNAPSHOT_PERIOD={snapshot_period}'.format( - snapshot_period=self.config['snapshot_period']) + f'EXPERIMENT_FILESTORE={self.config["experiment_filestore"]}') + + filestore = self.config['report_filestore'] + shared_report_filestore_arg = f'{filestore}:{filestore}' + set_report_filestore_arg = f'REPORT_FILESTORE={filestore}' + set_snapshot_period_arg = ( + f'SNAPSHOT_PERIOD={self.config["snapshot_period"]}') + docker_image_url = f'{docker_registry}/dispatcher-image' set_concurrent_builds_arg = ( f'CONCURRENT_BUILDS={self.config["concurrent_builds"]}') set_worker_pool_name_arg = ( f'WORKER_POOL_NAME={self.config["worker_pool_name"]}') - docker_image_url = '{docker_registry}/dispatcher-image'.format( - docker_registry=docker_registry) environment_args = [ '-e', 'LOCAL_EXPERIMENT=True', @@ -522,7 +519,7 @@ def start(self): '--shm-size=2g', '--cap-add=SYS_PTRACE', '--cap-add=SYS_NICE', - '--name=%s' % container_name, + f'--name={container_name}', docker_image_url, '/bin/bash', '-c', diff --git a/experiment/runner.py b/experiment/runner.py index 292fbb256..899adad14 100644 --- a/experiment/runner.py +++ b/experiment/runner.py @@ -116,7 +116,7 @@ def get_clusterfuzz_seed_corpus_path(fuzz_target_path): def _copy_custom_seed_corpus(corpus_directory): - "Copy custom seed corpus provided by user" + """Copy custom seed corpus provided by user""" shutil.rmtree(corpus_directory) benchmark = environment.get('BENCHMARK') benchmark_custom_corpus_dir = posixpath.join( @@ -162,7 +162,7 @@ def _unpack_clusterfuzz_seed_corpus(fuzz_target_path, corpus_directory): if seed_corpus_file.file_size > CORPUS_ELEMENT_BYTES_LIMIT: continue - output_filename = '%016d' % idx + output_filename = f'{idx:016d}' output_file_path = os.path.join(corpus_directory, output_filename) zip_file.extract(seed_corpus_file, output_file_path) idx += 1 @@ -208,13 +208,10 @@ def run_fuzzer(max_total_time, log_filename): command = [ 'nice', '-n', str(0 - runner_niceness), 'python3', '-u', '-c', - ('from fuzzers.{fuzzer} import fuzzer; ' + (f'from fuzzers.{environment.get("FUZZER")} import fuzzer; ' 'fuzzer.fuzz(' - "'{input_corpus}', '{output_corpus}', '{target_binary}')").format( - fuzzer=environment.get('FUZZER'), - input_corpus=shlex.quote(input_corpus), - output_corpus=shlex.quote(output_corpus), - target_binary=shlex.quote(target_binary)) + f'"{shlex.quote(input_corpus)}", "{shlex.quote(output_corpus)}", ' + f'"{shlex.quote(target_binary)}")') ] # Write output to stdout if user is fuzzing from command line. @@ -403,7 +400,7 @@ def record_stats(self): stats_filename = experiment_utils.get_stats_filename(self.cycle) stats_path = os.path.join(self.results_dir, stats_filename) - with open(stats_path, 'w') as stats_file_handle: + with open(stats_path, 'w', encoding='utf-8') as stats_file_handle: stats_file_handle.write(stats_json_str) def archive_corpus(self): @@ -463,7 +460,7 @@ def get_fuzzer_module(fuzzer): """Returns the fuzzer.py module for |fuzzer|. We made this function so that we can mock the module because importing modules makes hard to undo changes to the python process.""" - fuzzer_module_name = 'fuzzers.{fuzzer}.fuzzer'.format(fuzzer=fuzzer) + fuzzer_module_name = f'fuzzers.{fuzzer}.fuzzer' fuzzer_module = importlib.import_module(fuzzer_module_name) return fuzzer_module diff --git a/experiment/schedule_measure_workers.py b/experiment/schedule_measure_workers.py index d4cfc4e90..14e9a9e94 100644 --- a/experiment/schedule_measure_workers.py +++ b/experiment/schedule_measure_workers.py @@ -60,7 +60,7 @@ def initialize(experiment_config: dict): instance_template_name = get_measure_worker_instance_template_name( experiment) docker_image = posixpath.join(experiment_config['docker_registry'], - 'measure-worker:{}'.format(experiment)) + f'measure-worker:{experiment}') redis_host = experiment_config['redis_host'] experiment_filestore = experiment_config['experiment_filestore'] diff --git a/experiment/scheduler.py b/experiment/scheduler.py index 5213d844b..c503c1fc8 100644 --- a/experiment/scheduler.py +++ b/experiment/scheduler.py @@ -586,13 +586,13 @@ def schedule_loop(experiment_config: dict): if local_experiment: runner_num_cpu_cores = experiment_config['runner_num_cpu_cores'] processes = runners_cpus // runner_num_cpu_cores - logger.info('Scheduling runners from core 0 to %d.' % - (runner_num_cpu_cores * processes - 1)) + logger.info('Scheduling runners from core 0 to %d.', + runner_num_cpu_cores * processes - 1) core_allocation = {} for cpu in range(0, runner_num_cpu_cores * processes, runner_num_cpu_cores): - core_allocation['%d-%d' % - (cpu, cpu + runner_num_cpu_cores - 1)] = None + core_allocation[ + f'{cpu}-{cpu + runner_num_cpu_cores - 1}'] = None pool_args = (processes,) else: pool_args = (runners_cpus,) @@ -798,8 +798,8 @@ def create_trial_instance( # pylint: disable=too-many-arguments startup_script = render_startup_script_template(instance_name, fuzzer, benchmark, trial_id, experiment_config, cpuset) - startup_script_path = '/tmp/%s-start-docker.sh' % instance_name - with open(startup_script_path, 'w') as file_handle: + startup_script_path = f'/tmp/{instance_name}-start-docker.sh' + with open(startup_script_path, 'w', encoding='utf-8') as file_handle: file_handle.write(startup_script) return gcloud.create_instance(instance_name, @@ -817,7 +817,7 @@ def main(): }) if len(sys.argv) != 2: - print('Usage: {} '.format(sys.argv[0])) + print(f'Usage: {sys.argv[0]} ') return 1 experiment_config = yaml_utils.read(sys.argv[1]) diff --git a/experiment/stop_experiment.py b/experiment/stop_experiment.py index 44aed9935..01cd94de0 100644 --- a/experiment/stop_experiment.py +++ b/experiment/stop_experiment.py @@ -67,7 +67,7 @@ def stop_experiment(experiment_name, experiment_config_filename): def main(): """Stop the experiment.""" if len(sys.argv) != 3: - print("Usage {0} ") + print('Usage {0} ') return 1 logs.initialize() return 0 if stop_experiment(sys.argv[1], sys.argv[2]) else 1 diff --git a/experiment/test_runner.py b/experiment/test_runner.py index ed7082954..31482abbf 100644 --- a/experiment/test_runner.py +++ b/experiment/test_runner.py @@ -51,7 +51,7 @@ def test_run_fuzzer_log_file(mocked_communicate, fs, environ): assert mocked_popen.commands == [[ 'nice', '-n', '5', 'python3', '-u', '-c', 'from fuzzers.afl import fuzzer; ' - "fuzzer.fuzz('/out/seeds', '/out/corpus', '/out/fuzz-target')" + 'fuzzer.fuzz("/out/seeds", "/out/corpus", "/out/fuzz-target")' ]] assert os.path.exists(log_filename) @@ -105,9 +105,9 @@ def test_record_stats(trial_runner, fuzzer_module): cycle = 1337 trial_runner.cycle = cycle - stats_file = os.path.join(trial_runner.results_dir, 'stats-%d.json' % cycle) + stats_file = os.path.join(trial_runner.results_dir, f'stats-{cycle}.json') trial_runner.record_stats() - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_contents = file_handle.read() assert stats_file_contents == FuzzerAModule.DEFAULT_STATS @@ -126,7 +126,7 @@ class FuzzerAModuleNoGetStats: return_value=FuzzerAModuleNoGetStats): trial_runner.record_stats() - stats_file = os.path.join(trial_runner.results_dir, 'stats-%d.json' % cycle) + stats_file = os.path.join(trial_runner.results_dir, f'stats-{cycle}.json') assert not os.path.exists(stats_file) @@ -151,7 +151,7 @@ def get_stats(output_directory, log_filename): with mock.patch('common.logs.error') as mocked_log_error: trial_runner.record_stats() - stats_file = os.path.join(trial_runner.results_dir, 'stats-%d.json' % cycle) + stats_file = os.path.join(trial_runner.results_dir, f'stats-{cycle}.json') assert not os.path.exists(stats_file) mocked_log_error.assert_called_with('Stats are invalid.') @@ -175,7 +175,7 @@ def get_stats(output_directory, log_filename): return_value=FuzzerAModuleGetStatsException): trial_runner.record_stats() - stats_file = os.path.join(trial_runner.results_dir, 'stats-%d.json' % cycle) + stats_file = os.path.join(trial_runner.results_dir, f'stats-{cycle}.json') assert not os.path.exists(stats_file) mocked_log_error.assert_called_with( 'Call to %s failed.', FuzzerAModuleGetStatsException.get_stats) @@ -234,7 +234,7 @@ def test_do_sync_unchanged(mocked_is_corpus_dir_same, mocked_debug, mocked_debug.assert_any_call('Cycle: %d unchanged.', trial_runner.cycle) unchanged_cycles_path = os.path.join(trial_runner.results_dir, 'unchanged-cycles') - with open(unchanged_cycles_path) as file_handle: + with open(unchanged_cycles_path, encoding='utf-8') as file_handle: assert str(trial_runner.cycle) == file_handle.read().strip() assert not os.listdir(trial_runner.corpus_archives_dir) @@ -312,7 +312,7 @@ def test_is_corpus_dir_same_modified(trial_runner, fs): file_path = os.path.join(trial_runner.corpus_dir, 'f') fs.create_file(file_path) trial_runner._set_corpus_dir_contents() # pylint: disable=protected-access - with open(file_path, 'w') as file_handle: + with open(file_path, 'w', encoding='utf-8') as file_handle: file_handle.write('hi') assert not trial_runner.is_corpus_dir_same() @@ -323,8 +323,9 @@ class TestIntegrationRunner: @pytest.mark.skipif(not os.environ.get('TEST_EXPERIMENT_FILESTORE'), reason='TEST_EXPERIMENT_FILESTORE is not set, ' 'skipping integration test.') - @mock.patch('common.logs.error') # pylint: disable=no-self-use,too-many-locals + @mock.patch('common.logs.error') def test_integration_runner(self, mocked_error, tmp_path, environ): + # pylint: disable=too-many-locals """Test that runner can run libFuzzer and saves snapshots to GCS.""" # Switch cwd so that fuzzers don't create tons of files in the repo. os.chdir(tmp_path) @@ -351,7 +352,7 @@ def test_integration_runner(self, mocked_error, tmp_path, environ): experiment = 'integration-test-experiment' gcs_directory = posixpath.join(test_experiment_bucket, experiment, 'experiment-folders', - '%s-%s' % (benchmark, fuzzer), 'trial-1') + f'{benchmark}-{fuzzer}', 'trial-1') filestore_utils.rm(gcs_directory, force=True) # Add fuzzer directory to make it easy to run fuzzer.py in local # configuration. @@ -430,15 +431,13 @@ def test_clean_seed_corpus(fs): assert not os.path.exists(os.path.join(seed_corpus_dir, 'efg')) assert len(os.listdir(seed_corpus_dir)) == 3 # Directory 'a' and two files. - with open( - os.path.join( - seed_corpus_dir, - 'a9993e364706816aba3e25717850c26c9cd0d89d')) as file_handle: + with open(os.path.join(seed_corpus_dir, + 'a9993e364706816aba3e25717850c26c9cd0d89d'), + encoding='utf-8') as file_handle: assert file_handle.read() == 'abc' - with open( - os.path.join( - seed_corpus_dir, - '589c22335a381f122d129225f5c0ba3056ed5811')) as file_handle: + with open(os.path.join(seed_corpus_dir, + '589c22335a381f122d129225f5c0ba3056ed5811'), + encoding='utf-8') as file_handle: assert file_handle.read() == 'def' diff --git a/experiment/test_scheduler.py b/experiment/test_scheduler.py index cc7490b0b..b94be0e9c 100644 --- a/experiment/test_scheduler.py +++ b/experiment/test_scheduler.py @@ -193,7 +193,7 @@ def _test_create_trial_instance( # pylint: disable=too-many-locals scheduler.create_trial_instance(fuzzer_param, benchmark, trial, experiment_config, preemptible) instance_name = 'r-test-experiment-9' - expected_startup_script_path = '/tmp/%s-start-docker.sh' % instance_name + expected_startup_script_path = f'/tmp/{instance_name}-start-docker.sh' mocked_create_instance.assert_called_with( instance_name, @@ -202,7 +202,7 @@ def _test_create_trial_instance( # pylint: disable=too-many-locals startup_script=expected_startup_script_path, preemptible=preemptible) - with open(expected_startup_script_path) as file_handle: + with open(expected_startup_script_path, encoding='utf-8') as file_handle: content = file_handle.read() check_from = '# Start docker.' assert check_from in content @@ -223,7 +223,7 @@ def test_start_trials_not_started(mocked_create_instance, pending_trials, mocked_create_instance.return_value = False with ThreadPool() as pool: result = scheduler.start_trials(pending_trials, experiment_config, pool) - assert result == [] + assert not result @mock.patch('common.new_process.execute') @@ -373,7 +373,7 @@ def test_get_preempted_trials_nonpreemptible(experiment_config, db): """Tests that TrialInstanceManager.get_preempted_trials returns no trials for a nonpreemptible experiment.""" trial_instance_manager = get_trial_instance_manager(experiment_config) - assert trial_instance_manager.get_preempted_trials() == [] + assert not trial_instance_manager.get_preempted_trials() @mock.patch('common.gce._get_instance_items', return_value=[]) @@ -392,7 +392,7 @@ def test_get_preempted_trials_stale_preempted(_, preempt_exp_conf): 'experiment.scheduler.TrialInstanceManager.' '_get_started_unfinished_instances', return_value=[instance_name]): - assert trial_instance_manager.get_preempted_trials() == [] + assert not trial_instance_manager.get_preempted_trials() def _get_preempted_instance_item(trial_id, exp_conf): diff --git a/fuzzbench/run_experiment.py b/fuzzbench/run_experiment.py index 6a2c60787..fc4b2c76c 100644 --- a/fuzzbench/run_experiment.py +++ b/fuzzbench/run_experiment.py @@ -36,8 +36,9 @@ def run_experiment(config): for name, image in images_to_build.items(): depends = image.get('depends_on', None) if depends is not None: - assert len(depends) == 1, 'image %s has %d dependencies. Multiple '\ - 'dependencies are currently not supported.' % (name, len(depends)) + assert len(depends) == 1, ( + f'image {name} has {len(depends)} dependencies. ' + 'Multiple dependencies are currently not supported.') jobs_list.append( queue.enqueue( jobs.build_image, @@ -49,13 +50,13 @@ def run_experiment(config): while True: print('Current status of jobs:') - print('\tqueued:\t%d' % queue.count) - print('\tstarted:\t%d' % queue.started_job_registry.count) - print('\tdeferred:\t%d' % queue.deferred_job_registry.count) - print('\tfinished:\t%d' % queue.finished_job_registry.count) - print('\tfailed:\t%d' % queue.failed_job_registry.count) + print(f'\tqueued:\t{queue.count}') + print(f'\tstarted:\t{queue.started_job_registry.count}') + print(f'\tdeferred:\t{queue.deferred_job_registry.count}') + print(f'\tfinished:\t{queue.finished_job_registry.count}') + print(f'\tfailed:\t{queue.failed_job_registry.count}') for job in jobs_list: - print(' %s : %s\t(%s)' % (job.func_name, job.get_status(), job.id)) + print(f' {job.func_name} : {job.get_status()}\t({job.id})') if all([job.result is not None for job in jobs_list]): # pylint: disable=use-a-generator break @@ -65,7 +66,7 @@ def run_experiment(config): def main(): """Set up Redis connection and start the experiment.""" - redis_connection = redis.Redis(host="queue-server") + redis_connection = redis.Redis(host='queue-server') config_path = environment.get('EXPERIMENT_CONFIG', 'fuzzbench/local-experiment-config.yaml') diff --git a/fuzzbench/test_e2e/test_e2e_run.py b/fuzzbench/test_e2e/test_e2e_run.py index def9af807..2c77d77cb 100644 --- a/fuzzbench/test_e2e/test_e2e_run.py +++ b/fuzzbench/test_e2e/test_e2e_run.py @@ -38,7 +38,7 @@ def redis_connection(): return redis.Redis(host='queue-server') -# pylint: disable=no-self-use,redefined-outer-name +# pylint: disable=redefined-outer-name @pytest.mark.skipif('E2E_INTEGRATION_TEST' not in os.environ, reason='Not running end-to-end test.') @pytest.mark.usefixtures('redis_connection', 'experiment_config') diff --git a/fuzzbench/worker.py b/fuzzbench/worker.py index 531720d5d..6f5daf23f 100644 --- a/fuzzbench/worker.py +++ b/fuzzbench/worker.py @@ -20,7 +20,7 @@ def main(): """Sets up Redis connection and starts the worker.""" - redis_connection = redis.Redis(host="queue-server") + redis_connection = redis.Redis(host='queue-server') with rq.Connection(redis_connection): queue = rq.Queue('build_n_run_queue') worker = rq.Worker([queue]) diff --git a/fuzzers/afl/fuzzer.py b/fuzzers/afl/fuzzer.py index 484105489..18cb71229 100755 --- a/fuzzers/afl/fuzzer.py +++ b/fuzzers/afl/fuzzer.py @@ -51,7 +51,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument if not os.path.exists(stats_file): print('Can\'t find fuzzer_stats') return '{}' - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/afl_2_52_b/fuzzer.py b/fuzzers/afl_2_52_b/fuzzer.py index 853c0890c..7c4c44180 100755 --- a/fuzzers/afl_2_52_b/fuzzer.py +++ b/fuzzers/afl_2_52_b/fuzzer.py @@ -48,7 +48,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument """Gets fuzzer stats for AFL.""" # Get a dictionary containing the stats AFL reports. stats_file = os.path.join(output_corpus, 'fuzzer_stats') - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/afl_collision_free/fuzzer.py b/fuzzers/afl_collision_free/fuzzer.py new file mode 100755 index 000000000..8c4a5be4e --- /dev/null +++ b/fuzzers/afl_collision_free/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Sets AFL to avoid collisions + os.environ['AFL_COLLISION_FREE'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_double_timeout/fuzzer.py b/fuzzers/afl_double_timeout/fuzzer.py new file mode 100755 index 000000000..9876a0e47 --- /dev/null +++ b/fuzzers/afl_double_timeout/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Sets AFL timeout to be the double compared to the default heuristics + os.environ['AFL_DOUBLE_TIMEOUT'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_maxmap/fuzzer.py b/fuzzers/afl_maxmap/fuzzer.py new file mode 100755 index 000000000..7c4c44180 --- /dev/null +++ b/fuzzers/afl_maxmap/fuzzer.py @@ -0,0 +1,138 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_no_favfactor/fuzzer.py b/fuzzers/afl_no_favfactor/fuzzer.py new file mode 100755 index 000000000..ec2e3d5ac --- /dev/null +++ b/fuzzers/afl_no_favfactor/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Disables AFL corpus culling prioritization + os.environ['AFL_NO_FAV_FACTOR'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_no_favored/fuzzer.py b/fuzzers/afl_no_favored/fuzzer.py new file mode 100755 index 000000000..90c7a08ab --- /dev/null +++ b/fuzzers/afl_no_favored/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Disables AFL corpus culling + os.environ['AFL_NO_FAVORED'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_no_trim/fuzzer.py b/fuzzers/afl_no_trim/fuzzer.py new file mode 100755 index 000000000..7430d9316 --- /dev/null +++ b/fuzzers/afl_no_trim/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Disables AFL testcase trimming + os.environ['AFL_DISABLE_TRIM'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_random_favored/fuzzer.py b/fuzzers/afl_random_favored/fuzzer.py index 853c0890c..7c4c44180 100755 --- a/fuzzers/afl_random_favored/fuzzer.py +++ b/fuzzers/afl_random_favored/fuzzer.py @@ -48,7 +48,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument """Gets fuzzer stats for AFL.""" # Get a dictionary containing the stats AFL reports. stats_file = os.path.join(output_corpus, 'fuzzer_stats') - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/afl_scheduling_lifo/fuzzer.py b/fuzzers/afl_scheduling_lifo/fuzzer.py new file mode 100755 index 000000000..328b3b718 --- /dev/null +++ b/fuzzers/afl_scheduling_lifo/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Sets corpus scheduling with LIFO policy + os.environ['AFL_LIFO_CORPUS'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_scheduling_random/fuzzer.py b/fuzzers/afl_scheduling_random/fuzzer.py new file mode 100755 index 000000000..af9ebc313 --- /dev/null +++ b/fuzzers/afl_scheduling_random/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Sets AFL to use a random selection for the corpus scheduling + os.environ['AFL_RANDOMIC_CORPUS'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_score_max/fuzzer.py b/fuzzers/afl_score_max/fuzzer.py new file mode 100755 index 000000000..58b8860f4 --- /dev/null +++ b/fuzzers/afl_score_max/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Maximize the score + os.environ['AFL_MAX_ENERGY'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_score_min/fuzzer.py b/fuzzers/afl_score_min/fuzzer.py new file mode 100755 index 000000000..e0ec33354 --- /dev/null +++ b/fuzzers/afl_score_min/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Sets AFL minimum score + os.environ['AFL_MIN_ENERGY'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_score_no_novel_prioritization/fuzzer.py b/fuzzers/afl_score_no_novel_prioritization/fuzzer.py new file mode 100755 index 000000000..ab296b70a --- /dev/null +++ b/fuzzers/afl_score_no_novel_prioritization/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Disable handicap + os.environ['AFL_DISABLE_HANDICAP'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_score_random/fuzzer.py b/fuzzers/afl_score_random/fuzzer.py new file mode 100755 index 000000000..d951d749f --- /dev/null +++ b/fuzzers/afl_score_random/fuzzer.py @@ -0,0 +1,140 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + # Sets AFL score to random constant + os.environ['AFL_RANDOM_ENERGY'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_splicing_mutation/fuzzer.py b/fuzzers/afl_splicing_mutation/fuzzer.py new file mode 100755 index 000000000..7c4c44180 --- /dev/null +++ b/fuzzers/afl_splicing_mutation/fuzzer.py @@ -0,0 +1,138 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with afl-fuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/afl_virginmap/fuzzer.py b/fuzzers/afl_virginmap/fuzzer.py index 853c0890c..7c4c44180 100755 --- a/fuzzers/afl_virginmap/fuzzer.py +++ b/fuzzers/afl_virginmap/fuzzer.py @@ -48,7 +48,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument """Gets fuzzer stats for AFL.""" # Get a dictionary containing the stats AFL reports. stats_file = os.path.join(output_corpus, 'fuzzer_stats') - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/aflcc/fuzzer.py b/fuzzers/aflcc/fuzzer.py index 16852f096..b402d5ff1 100644 --- a/fuzzers/aflcc/fuzzer.py +++ b/fuzzers/aflcc/fuzzer.py @@ -154,11 +154,11 @@ def prepare_build_environment(): def post_build(fuzz_target): """Perform the post-processing for a target""" - print('Fuzz-target: {target}'.format(target=fuzz_target)) + print(f'Fuzz-target: {fuzz_target}') - getbc_cmd = "/afl/aflc-get-bc {target}".format(target=fuzz_target) + getbc_cmd = f'/afl/aflc-get-bc {fuzz_target}' if os.system(getbc_cmd) != 0: - raise ValueError("get-bc failed") + raise ValueError('get-bc failed') # Set the flags. ldflags is here temporarily until the benchmarks # are cleaned up and standalone. @@ -192,7 +192,7 @@ def post_build(fuzz_target): target=fuzz_target, ldflags=ldflags) if os.system(bin1_cmd) != 0: - raise ValueError('command "{command}" failed'.format(command=bin1_cmd)) + raise ValueError(f'command "{bin1_cmd}" failed') # The normalized build with non-optimized dictionary. print('[post_build] Generating normalized-none-nopt') @@ -206,7 +206,7 @@ def post_build(fuzz_target): target=fuzz_target, ldflags=ldflags) if os.system(bin2_cmd) != 0: - raise ValueError('command "{command}" failed'.format(command=bin2_cmd)) + raise ValueError(f'command "{bin2_cmd}" failed') # The no-collision split-condition optimized dictionary. print('[post_build] Generating no-collision-all-opt build') @@ -220,7 +220,7 @@ def post_build(fuzz_target): target=fuzz_target, ldflags=ldflags) if os.system(bin3_cmd) != 0: - raise ValueError('command "{command}" failed'.format(command=bin3_cmd)) + raise ValueError(f'command "{bin3_cmd}" failed') print('[post_build] Copying afl-fuzz to $OUT directory') # Copy out the afl-fuzz binary as a build artifact. @@ -285,30 +285,29 @@ def fuzz(input_corpus, output_corpus, target_binary): # Use a dictionary for original afl as well. print('[fuzz] Running AFL for original binary') - src_file = '{target}-normalized-none-nopt.dict'.format(target=target_binary) - dst_file = '{target}-original.dict'.format(target=target_binary) + src_file = f'{target_binary}-normalized-none-nopt.dict' + dst_file = f'{target_binary}-original.dict' shutil.copy(src_file, dst_file) # Instead of generating a new dict, just hack this one # to be non-optimized to prevent AFL from aborting. - os.system('sed -i \'s/OPTIMIZED/NORMAL/g\' {dict}'.format(dict=dst_file)) - afl_fuzz_thread1 = threading.Thread( - target=run_fuzzer, - args=(input_corpus, output_corpus, - '{target}-original'.format(target=target_binary), - ['-S', 'secondary-original'])) + os.system(f'sed -i \'s/OPTIMIZED/NORMAL/g\' {dst_file}') + afl_fuzz_thread1 = threading.Thread(target=run_fuzzer, + args=(input_corpus, output_corpus, + f'{target_binary}-original', + ['-S', 'secondary-original'])) afl_fuzz_thread1.start() print('[run_fuzzer] Running AFL for normalized and optimized dictionary') afl_fuzz_thread2 = threading.Thread( target=run_fuzzer, args=(input_corpus, output_corpus, - '{target}-normalized-none-nopt'.format(target=target_binary), + f'{target_binary}-normalized-none-nopt', ['-S', 'secondary-normalized-nopt'])) afl_fuzz_thread2.start() print('[run_fuzzer] Running AFL for FBSP and optimized dictionary') run_fuzzer(input_corpus, output_corpus, - '{target}-no-collision-all-opt'.format(target=target_binary), + f'{target_binary}-no-collision-all-opt', ['-S', 'secondary-no-collision-all-opt'], hide_output=False) diff --git a/fuzzers/aflplusplus/fuzzer.py b/fuzzers/aflplusplus/fuzzer.py index 7fc4a41e2..55bc4b4c0 100755 --- a/fuzzers/aflplusplus/fuzzer.py +++ b/fuzzers/aflplusplus/fuzzer.py @@ -37,10 +37,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements # a default configuration. # Add required libs for libpcap_fuzz_both. - os.environ['EXTRA_LIBS'] = ( - '/usr/lib/x86_64-linux-gnu/libdbus-1.a ' - '/lib/x86_64-linux-gnu/libsystemd.so.0' - ) + os.environ['EXTRA_LIBS'] = ('/usr/lib/x86_64-linux-gnu/libdbus-1.a ' + '/lib/x86_64-linux-gnu/libsystemd.so.0') build_modes = list(args) if 'BUILD_MODES' in os.environ: @@ -165,8 +163,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -212,12 +210,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For symcc build, set the OUT and FUZZ_TARGET environment # variable to point to the new symcc build directory. @@ -271,7 +269,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/aflplusplus_cmplog/fuzzer.py b/fuzzers/aflplusplus_cmplog/fuzzer.py index 7f4c55f35..f5d88c0f1 100755 --- a/fuzzers/aflplusplus_cmplog/fuzzer.py +++ b/fuzzers/aflplusplus_cmplog/fuzzer.py @@ -25,7 +25,7 @@ def build(): # pylint: disable=too-many-branches,too-many-statements """Build benchmark.""" - aflplusplus_fuzzer.build("tracepc", "cmplog") + aflplusplus_fuzzer.build('tracepc', 'cmplog') def fuzz(input_corpus, output_corpus, target_binary): diff --git a/fuzzers/aflplusplus_dict2file/fuzzer.py b/fuzzers/aflplusplus_dict2file/fuzzer.py index ccbd4f5e1..f1c6887e6 100755 --- a/fuzzers/aflplusplus_dict2file/fuzzer.py +++ b/fuzzers/aflplusplus_dict2file/fuzzer.py @@ -25,7 +25,7 @@ def build(): # pylint: disable=too-many-branches,too-many-statements """Build benchmark.""" - aflplusplus_fuzzer.build("tracepc", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'dict2file') def fuzz(input_corpus, output_corpus, target_binary): diff --git a/fuzzers/aflplusplus_frida/fuzzer.py b/fuzzers/aflplusplus_frida/fuzzer.py index eaa5a8018..520bbdbf2 100755 --- a/fuzzers/aflplusplus_frida/fuzzer.py +++ b/fuzzers/aflplusplus_frida/fuzzer.py @@ -36,7 +36,7 @@ def fuzz(input_corpus, output_corpus, target_binary): ], stdout=subprocess.PIPE, check=True) - target_func = nm_proc.stdout.split()[0].decode("utf-8") + target_func = nm_proc.stdout.split()[0].decode('utf-8') print('[fuzz] LLVMFuzzerTestOneInput() address =', target_func) # Fuzzer options for qemu_mode. @@ -44,9 +44,9 @@ def fuzz(input_corpus, output_corpus, target_binary): os.environ['AFL_FRIDA_PERSISTENT_ADDR'] = target_func os.environ['AFL_ENTRYPOINT'] = target_func - os.environ['AFL_FRIDA_PERSISTENT_CNT'] = "1000000" - os.environ['AFL_FRIDA_PERSISTENT_HOOK'] = "/out/frida_hook.so" - os.environ['AFL_PATH'] = "/out" + os.environ['AFL_FRIDA_PERSISTENT_CNT'] = '1000000' + os.environ['AFL_FRIDA_PERSISTENT_HOOK'] = '/out/frida_hook.so' + os.environ['AFL_PATH'] = '/out' # resource.setrlimit(resource.RLIMIT_CORE, # (resource.RLIM_INFINITY, resource.RLIM_INFINITY)) @@ -54,13 +54,13 @@ def fuzz(input_corpus, output_corpus, target_binary): # The systemd benchmark fails without full library instrumentation :( benchmark_name = os.environ['BENCHMARK'] if benchmark_name == 'systemd_fuzz-link-parser': - os.environ['AFL_INST_LIBS'] = "1" + os.environ['AFL_INST_LIBS'] = '1' aflplusplus_fuzzer.fuzz(input_corpus, output_corpus, target_binary, flags=flags) - # sts = os.system("cp -v *core* corpus") + # sts = os.system('cp -v *core* corpus') # if sts == 0: # print('Copied cores') diff --git a/fuzzers/aflplusplus_optimal/fuzzer.py b/fuzzers/aflplusplus_optimal/fuzzer.py index c298b2060..425b50944 100755 --- a/fuzzers/aflplusplus_optimal/fuzzer.py +++ b/fuzzers/aflplusplus_optimal/fuzzer.py @@ -32,49 +32,49 @@ def build(): # pylint: disable=too-many-branches,too-many-statements benchmark_name = os.environ['BENCHMARK'] if benchmark_name == 'bloaty_fuzz_target': - aflplusplus_fuzzer.build("lto") + aflplusplus_fuzzer.build('lto') elif benchmark_name == 'curl_curl_fuzzer_http': - aflplusplus_fuzzer.build("lto") + aflplusplus_fuzzer.build('lto') elif benchmark_name == 'freetype2-2017': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'harfbuzz-1.3.2': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'jsoncpp_jsoncpp_fuzzer': - aflplusplus_fuzzer.build("lto", "laf") + aflplusplus_fuzzer.build('lto', 'laf') elif benchmark_name == 'lcms-2017-03-21': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'libjpeg-turbo-07-2017': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'libxslt_xpath': - aflplusplus_fuzzer.build("lto", "cmplog") + aflplusplus_fuzzer.build('lto', 'cmplog') elif benchmark_name == 'openh264_decoder_fuzzer': - aflplusplus_fuzzer.build("lto", "cmplog") + aflplusplus_fuzzer.build('lto', 'cmplog') elif benchmark_name == 'openssl_x509': - aflplusplus_fuzzer.build("tracepc", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'dict2file') elif benchmark_name == 'php_php-fuzz-parser': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'proj4-2017-08-14': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'sqlite3_ossfuzz': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'stb_stbi_read_fuzzer': - aflplusplus_fuzzer.build("lto", "cmplog") + aflplusplus_fuzzer.build('lto', 'cmplog') elif benchmark_name == 'systemd_fuzz-link-parser': - aflplusplus_fuzzer.build("tracepc", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'dict2file') elif benchmark_name == 'vorbis-2017-12-11': - aflplusplus_fuzzer.build("lto", "laf") + aflplusplus_fuzzer.build('lto', 'laf') elif benchmark_name == 'woff2-2016-05-06': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') elif benchmark_name == 'zlib_zlib_uncompress_fuzzer': - aflplusplus_fuzzer.build("tracepc", "cmplog", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'cmplog', 'dict2file') else: build_flags = os.environ['CFLAGS'] if build_flags.find('array-bounds') != -1: - aflplusplus_fuzzer.build("tracepc", "dict2file") + aflplusplus_fuzzer.build('tracepc', 'dict2file') else: - aflplusplus_fuzzer.build("lto", "cmplog") + aflplusplus_fuzzer.build('lto', 'cmplog') - for copy_file in glob.glob("/afl/libc*"): + for copy_file in glob.glob('/afl/libc*'): shutil.copy(copy_file, os.environ['OUT']) diff --git a/fuzzers/aflplusplus_qemu/fuzzer.py b/fuzzers/aflplusplus_qemu/fuzzer.py index ca1dd7902..f2f6c2945 100755 --- a/fuzzers/aflplusplus_qemu/fuzzer.py +++ b/fuzzers/aflplusplus_qemu/fuzzer.py @@ -33,7 +33,7 @@ def fuzz(input_corpus, output_corpus, target_binary): ], stdout=subprocess.PIPE, check=True) - target_func = "0x" + nm_proc.stdout.split()[0].decode("utf-8") + target_func = '0x' + nm_proc.stdout.split()[0].decode('utf-8') print('[fuzz] afl_qemu_driver_stdin_input() address =', target_func) # Fuzzer options for qemu_mode. @@ -41,8 +41,8 @@ def fuzz(input_corpus, output_corpus, target_binary): os.environ['AFL_QEMU_PERSISTENT_ADDR'] = target_func os.environ['AFL_ENTRYPOINT'] = target_func - os.environ['AFL_QEMU_PERSISTENT_CNT'] = "1000000" - os.environ['AFL_QEMU_DRIVER_NO_HOOK'] = "1" + os.environ['AFL_QEMU_PERSISTENT_CNT'] = '1000000' + os.environ['AFL_QEMU_DRIVER_NO_HOOK'] = '1' aflplusplus_fuzzer.fuzz(input_corpus, output_corpus, target_binary, diff --git a/fuzzers/aflplusplus_qemu_tracepc/fuzzer.py b/fuzzers/aflplusplus_qemu_tracepc/fuzzer.py index b9bfec40c..e2f5908c5 100755 --- a/fuzzers/aflplusplus_qemu_tracepc/fuzzer.py +++ b/fuzzers/aflplusplus_qemu_tracepc/fuzzer.py @@ -33,7 +33,7 @@ def fuzz(input_corpus, output_corpus, target_binary): ], stdout=subprocess.PIPE, check=True) - target_func = "0x" + nm_proc.stdout.split()[0].decode("utf-8") + target_func = '0x' + nm_proc.stdout.split()[0].decode('utf-8') print('[fuzz] afl_qemu_driver_stdin_input() address =', target_func) # Fuzzer options for qemu_mode. @@ -41,8 +41,8 @@ def fuzz(input_corpus, output_corpus, target_binary): os.environ['AFL_QEMU_PERSISTENT_ADDR'] = target_func os.environ['AFL_ENTRYPOINT'] = target_func - os.environ['AFL_QEMU_PERSISTENT_CNT'] = "1000000" - os.environ['AFL_QEMU_DRIVER_NO_HOOK'] = "1" + os.environ['AFL_QEMU_PERSISTENT_CNT'] = '1000000' + os.environ['AFL_QEMU_DRIVER_NO_HOOK'] = '1' aflplusplus_fuzzer.fuzz(input_corpus, output_corpus, target_binary, diff --git a/fuzzers/aflplusplus_zafl/fuzzer.py b/fuzzers/aflplusplus_zafl/fuzzer.py index 2217e9c0c..113e8eb70 100755 --- a/fuzzers/aflplusplus_zafl/fuzzer.py +++ b/fuzzers/aflplusplus_zafl/fuzzer.py @@ -31,9 +31,9 @@ def build(): os.environ['CC'] = '/cc.sh' os.environ['CXX'] = '/cxx.sh' if 'LD_LIBRARY_PATH' in os.environ: - os.environ['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH'] + ":/out" + os.environ['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH'] + ':/out' else: - os.environ['LD_LIBRARY_PATH'] = "/out" + os.environ['LD_LIBRARY_PATH'] = '/out' utils.append_flags('CFLAGS', ['-fPIC', '-lpthread']) utils.append_flags('CXXFLAGS', ['-fPIC', '-lpthread']) diff --git a/fuzzers/aflpp_random_default/fuzzer.py b/fuzzers/aflpp_random_default/fuzzer.py index 354c5457e..f51c59195 100755 --- a/fuzzers/aflpp_random_default/fuzzer.py +++ b/fuzzers/aflpp_random_default/fuzzer.py @@ -150,8 +150,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -197,12 +197,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For CmpLog build, set the OUT and FUZZ_TARGET environment # variable to point to the new CmpLog build directory. @@ -256,7 +256,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/aflpp_random_no_favs/fuzzer.py b/fuzzers/aflpp_random_no_favs/fuzzer.py index d2411e822..d8a93b36c 100755 --- a/fuzzers/aflpp_random_no_favs/fuzzer.py +++ b/fuzzers/aflpp_random_no_favs/fuzzer.py @@ -150,8 +150,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -197,12 +197,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For CmpLog build, set the OUT and FUZZ_TARGET environment # variable to point to the new CmpLog build directory. @@ -256,7 +256,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/aflpp_random_wrs/fuzzer.py b/fuzzers/aflpp_random_wrs/fuzzer.py index c551c1af4..f561625fa 100755 --- a/fuzzers/aflpp_random_wrs/fuzzer.py +++ b/fuzzers/aflpp_random_wrs/fuzzer.py @@ -150,8 +150,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -197,12 +197,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For CmpLog build, set the OUT and FUZZ_TARGET environment # variable to point to the new CmpLog build directory. @@ -256,7 +256,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/aflpp_random_wrs_rf/fuzzer.py b/fuzzers/aflpp_random_wrs_rf/fuzzer.py index b350390e2..50a073a99 100755 --- a/fuzzers/aflpp_random_wrs_rf/fuzzer.py +++ b/fuzzers/aflpp_random_wrs_rf/fuzzer.py @@ -150,8 +150,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -197,12 +197,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For CmpLog build, set the OUT and FUZZ_TARGET environment # variable to point to the new CmpLog build directory. @@ -256,7 +256,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/aflpp_random_wrs_rf_rp/fuzzer.py b/fuzzers/aflpp_random_wrs_rf_rp/fuzzer.py index 354c5457e..f51c59195 100755 --- a/fuzzers/aflpp_random_wrs_rf_rp/fuzzer.py +++ b/fuzzers/aflpp_random_wrs_rf_rp/fuzzer.py @@ -150,8 +150,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -197,12 +197,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For CmpLog build, set the OUT and FUZZ_TARGET environment # variable to point to the new CmpLog build directory. @@ -256,7 +256,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/aflpp_random_wrs_rp/fuzzer.py b/fuzzers/aflpp_random_wrs_rp/fuzzer.py index 2d0e41b19..e6fe85980 100755 --- a/fuzzers/aflpp_random_wrs_rp/fuzzer.py +++ b/fuzzers/aflpp_random_wrs_rp/fuzzer.py @@ -150,8 +150,8 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements else: os.environ['FUZZER_LIB'] = '/libAFLDriver.a' - # Some benchmarks like lcms - # (see: https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) + # Some benchmarks like lcms. (see: + # https://github.com/mm2/Little-CMS/commit/ab1093539b4287c233aca6a3cf53b234faceb792#diff-f0e6d05e72548974e852e8e55dffc4ccR212) # fail to compile if the compiler outputs things to stderr in unexpected # cases. Prevent these failures by using AFL_QUIET to stop afl-clang-fast # from writing AFL specific messages to stderr. @@ -197,12 +197,12 @@ def build(*args): # pylint: disable=too-many-branches,too-many-statements new_env['CC'] = '/symcc/build/symcc' new_env['CXX'] = '/symcc/build/sym++' new_env['SYMCC_OUTPUT_DIR'] = '/tmp' - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_directory - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" - new_env['SYMCC_SILENT'] = "1" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' + new_env['SYMCC_SILENT'] = '1' # For CmpLog build, set the OUT and FUZZ_TARGET environment # variable to point to the new CmpLog build directory. @@ -256,7 +256,7 @@ def fuzz(input_corpus, flags += ['-c', cmplog_target_binary] if not skip: - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # os.environ['AFL_FAST_CAL'] = '1' os.environ['AFL_CMPLOG_ONLY_NEW'] = '1' if 'ADDITIONAL_ARGS' in os.environ: diff --git a/fuzzers/centipede/fuzzer.py b/fuzzers/centipede/fuzzer.py index b0b89c6a9..63b9cd7f6 100755 --- a/fuzzers/centipede/fuzzer.py +++ b/fuzzers/centipede/fuzzer.py @@ -32,7 +32,8 @@ def build(): ] # TODO(Dongge): Build targets with sanitizers. - with open('/src/centipede/clang-flags.txt', 'r') as clang_flags_handle: + with open('/src/centipede/clang-flags.txt', 'r', + encoding='utf-8') as clang_flags_handle: centipede_cflags = [ line.strip() for line in clang_flags_handle.readlines() ] diff --git a/fuzzers/centipede_function_filter/fuzzer.py b/fuzzers/centipede_function_filter/fuzzer.py index e72cd24a2..7aa904996 100755 --- a/fuzzers/centipede_function_filter/fuzzer.py +++ b/fuzzers/centipede_function_filter/fuzzer.py @@ -26,7 +26,7 @@ def build(): def fuzz(input_corpus, output_corpus, target_binary): """Run fuzzer. Wrapper that uses the defaults when calling run_fuzzer.""" - with open('/focus_map.yaml', 'r') as focus_file: + with open('/focus_map.yaml', 'r', encoding='utf-8') as focus_file: focus_map = yaml.safe_load(focus_file) benchmark = os.getenv('BENCHMARK', None) if benchmark not in focus_map: diff --git a/fuzzers/eclipser/fuzzer.py b/fuzzers/eclipser/fuzzer.py index c36d898b1..19e69f6fa 100644 --- a/fuzzers/eclipser/fuzzer.py +++ b/fuzzers/eclipser/fuzzer.py @@ -72,7 +72,7 @@ def eclipser(input_corpus, output_corpus, target_binary): # We will use output_corpus as a directory where AFL and Eclipser sync their # test cases with each other. For Eclipser, we should explicitly specify an # output directory under this sync directory. - eclipser_out = os.path.join(output_corpus, "eclipser_output") + eclipser_out = os.path.join(output_corpus, 'eclipser_output') command = [ 'dotnet', '/Eclipser/build/Eclipser.dll', @@ -94,7 +94,8 @@ def eclipser(input_corpus, output_corpus, target_binary): if os.listdir(input_corpus): # Specify inputs only if any seed exists. command += ['-i', input_corpus] print('[eclipser] Run Eclipser with command: ' + ' '.join(command)) - subprocess.Popen(command) + with subprocess.Popen(command): + pass def afl_worker(input_corpus, output_corpus, target_binary): diff --git a/fuzzers/eclipser_aflplusplus/fuzzer.py b/fuzzers/eclipser_aflplusplus/fuzzer.py index 6ea6ba318..87235b012 100644 --- a/fuzzers/eclipser_aflplusplus/fuzzer.py +++ b/fuzzers/eclipser_aflplusplus/fuzzer.py @@ -38,7 +38,7 @@ def build(): fuzz_target = os.getenv('FUZZ_TARGET') # First, build an uninstrumented binary for Eclipser. - aflplusplus_fuzzer.build("qemu", "eclipser") + aflplusplus_fuzzer.build('qemu', 'eclipser') eclipser_dir = get_uninstrumented_outdir(build_directory) os.mkdir(eclipser_dir) fuzz_binary = build_directory + '/' + fuzz_target @@ -48,7 +48,7 @@ def build(): # Second, build an instrumented binary for AFL++. os.environ = orig_env - aflplusplus_fuzzer.build("tracepc") + aflplusplus_fuzzer.build('tracepc') print('[build] Copying afl-fuzz to $OUT directory') # Copy afl-fuzz @@ -60,7 +60,7 @@ def eclipser(input_corpus, output_corpus, target_binary): # We will use output_corpus as a directory where AFL and Eclipser sync their # test cases with each other. For Eclipser, we should explicitly specify an # output directory under this sync directory. - eclipser_out = os.path.join(output_corpus, "eclipser_output") + eclipser_out = os.path.join(output_corpus, 'eclipser_output') command = [ 'dotnet', '/Eclipser/build/Eclipser.dll', @@ -82,7 +82,8 @@ def eclipser(input_corpus, output_corpus, target_binary): if os.listdir(input_corpus): # Specify inputs only if any seed exists. command += ['-i', input_corpus] print('[eclipser] Run Eclipser with command: ' + ' '.join(command)) - subprocess.Popen(command) + with subprocess.Popen(command): + pass def afl_worker(input_corpus, output_corpus, target_binary): @@ -105,13 +106,13 @@ def fuzz(input_corpus, output_corpus, target_binary): uninstrumented_target_binary = os.path.join( uninstrumented_target_binary_directory, target_binary_name) if not os.path.isdir(input_corpus): - raise Exception("invalid input directory") + raise Exception('invalid input directory') afl_args = (input_corpus, output_corpus, target_binary) eclipser_args = (input_corpus, output_corpus, uninstrumented_target_binary) # Do not launch AFL master instance for now, to reduce memory usage and # align with the vanilla AFL. - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' print('[fuzz] Running AFL worker') afl_worker_thread = threading.Thread(target=afl_worker, args=afl_args) afl_worker_thread.start() diff --git a/fuzzers/fafuzz/fuzzer.py b/fuzzers/fafuzz/fuzzer.py index a1116a55e..8713d76d2 100644 --- a/fuzzers/fafuzz/fuzzer.py +++ b/fuzzers/fafuzz/fuzzer.py @@ -48,7 +48,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument """Gets fuzzer stats for AFL.""" # Get a dictionary containing the stats AFL reports. stats_file = os.path.join(output_corpus, 'fuzzer_stats') - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/fuzzolic_aflplusplus_fuzzy/fuzzer.py b/fuzzers/fuzzolic_aflplusplus_fuzzy/fuzzer.py index 838fbf053..e3e785e2c 100644 --- a/fuzzers/fuzzolic_aflplusplus_fuzzy/fuzzer.py +++ b/fuzzers/fuzzolic_aflplusplus_fuzzy/fuzzer.py @@ -78,9 +78,9 @@ def fuzzolic(input_corpus, output_corpus, target_binary): # test cases with each other. For Fuzzolic, we should explicitly specify an # output directory under this sync directory. if input_corpus: - fuzzolic_out = os.path.join(output_corpus, "fuzzolic_output") - afl_out = os.path.join(output_corpus, "afl-worker") - afl_queue = os.path.join(afl_out, "queue") + fuzzolic_out = os.path.join(output_corpus, 'fuzzolic_output') + afl_out = os.path.join(output_corpus, 'afl-worker') + afl_queue = os.path.join(afl_out, 'queue') command = [ '/out/fuzzolic/fuzzolic/fuzzolic.py', '-f', # fuzzy-sat solver @@ -99,7 +99,8 @@ def fuzzolic(input_corpus, output_corpus, target_binary): target_binary, ] print('[fuzzolic] Running Fuzzolic with command: ' + ' '.join(command)) - subprocess.Popen(command) + with subprocess.Popen(command): + pass def afl_worker(input_corpus, output_corpus, target_binary): @@ -118,7 +119,7 @@ def fuzz(input_corpus, output_corpus, target_binary): afl_fuzzer.prepare_fuzz_environment(input_corpus) print('[fuzz] Running AFL worker') - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' afl_args = (input_corpus, output_corpus, target_binary) afl_worker_thread = threading.Thread(target=afl_worker, args=afl_args) afl_worker_thread.start() diff --git a/fuzzers/fuzzolic_aflplusplus_z3/fuzzer.py b/fuzzers/fuzzolic_aflplusplus_z3/fuzzer.py index 2fec3bb33..5fe9ff931 100644 --- a/fuzzers/fuzzolic_aflplusplus_z3/fuzzer.py +++ b/fuzzers/fuzzolic_aflplusplus_z3/fuzzer.py @@ -78,9 +78,9 @@ def fuzzolic(input_corpus, output_corpus, target_binary): # test cases with each other. For Fuzzolic, we should explicitly specify an # output directory under this sync directory. if input_corpus: - fuzzolic_out = os.path.join(output_corpus, "fuzzolic_output") - afl_out = os.path.join(output_corpus, "afl-worker") - afl_queue = os.path.join(afl_out, "queue") + fuzzolic_out = os.path.join(output_corpus, 'fuzzolic_output') + afl_out = os.path.join(output_corpus, 'afl-worker') + afl_queue = os.path.join(afl_out, 'queue') command = [ '/out/fuzzolic/fuzzolic/fuzzolic.py', '-p', # optimistic solving @@ -98,7 +98,8 @@ def fuzzolic(input_corpus, output_corpus, target_binary): target_binary, ] print('[fuzzolic] Running Fuzzolic with command: ' + ' '.join(command)) - subprocess.Popen(command) + with subprocess.Popen(command): + pass def afl_worker(input_corpus, output_corpus, target_binary): @@ -117,7 +118,7 @@ def fuzz(input_corpus, output_corpus, target_binary): afl_fuzzer.prepare_fuzz_environment(input_corpus) print('[fuzz] Running AFL worker') - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' afl_args = (input_corpus, output_corpus, target_binary) afl_worker_thread = threading.Thread(target=afl_worker, args=afl_args) afl_worker_thread.start() diff --git a/fuzzers/gramatron/fuzzer.py b/fuzzers/gramatron/fuzzer.py index e4cf668d7..3174f8894 100755 --- a/fuzzers/gramatron/fuzzer.py +++ b/fuzzers/gramatron/fuzzer.py @@ -23,18 +23,18 @@ def prepare_fuzz_environment(input_corpus): """Prepare to fuzz with a LibAFL-based fuzzer.""" - os.environ['ASAN_OPTIONS'] = "abort_on_error=1:detect_leaks=0:"\ - "malloc_context_size=0:symbolize=0:"\ - "allocator_may_return_null=1:"\ - "detect_odr_violation=0:handle_segv=0:"\ - "handle_sigbus=0:handle_abort=0:"\ - "handle_sigfpe=0:handle_sigill=0" - os.environ['UBSAN_OPTIONS'] = "abort_on_error=1:"\ - "allocator_release_to_os_interval_ms=500:"\ - "handle_abort=0:handle_segv=0:"\ - "handle_sigbus=0:handle_sigfpe=0:"\ - "handle_sigill=0:print_stacktrace=0:"\ - "symbolize=0:symbolize_inline_frames=0" + os.environ['ASAN_OPTIONS'] = 'abort_on_error=1:detect_leaks=0:'\ + 'malloc_context_size=0:symbolize=0:'\ + 'allocator_may_return_null=1:'\ + 'detect_odr_violation=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_abort=0:'\ + 'handle_sigfpe=0:handle_sigill=0' + os.environ['UBSAN_OPTIONS'] = 'abort_on_error=1:'\ + 'allocator_release_to_os_interval_ms=500:'\ + 'handle_abort=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_sigfpe=0:'\ + 'handle_sigill=0:print_stacktrace=0:'\ + 'symbolize=0:symbolize_inline_frames=0' # Create at least one non-empty seed to start. utils.create_seed_file_for_empty_corpus(input_corpus) @@ -52,7 +52,7 @@ def build(): # pylint: disable=too-many-branches,too-many-statements raise RuntimeError('Unsupported benchmark, unavailable grammar') dest = os.path.join(os.environ['OUT'], 'grammar.json.gz') shutil.copy(copy_file, dest) - os.system("gzip -d '%s'" % dest) + os.system(f'gzip -d "{dest}"') os.environ['CC'] = '/libafl_fuzzbench/target/release/gramatron_cc' os.environ['CXX'] = '/libafl_fuzzbench/target/release/gramatron_cxx' diff --git a/fuzzers/grimoire/fuzzer.py b/fuzzers/grimoire/fuzzer.py index 87f9d7f54..1c156412a 100755 --- a/fuzzers/grimoire/fuzzer.py +++ b/fuzzers/grimoire/fuzzer.py @@ -23,18 +23,18 @@ def prepare_fuzz_environment(input_corpus): """Prepare to fuzz with a LibAFL-based fuzzer.""" - os.environ['ASAN_OPTIONS'] = "abort_on_error=1:detect_leaks=0:"\ - "malloc_context_size=0:symbolize=0:"\ - "allocator_may_return_null=1:"\ - "detect_odr_violation=0:handle_segv=0:"\ - "handle_sigbus=0:handle_abort=0:"\ - "handle_sigfpe=0:handle_sigill=0" - os.environ['UBSAN_OPTIONS'] = "abort_on_error=1:"\ - "allocator_release_to_os_interval_ms=500:"\ - "handle_abort=0:handle_segv=0:"\ - "handle_sigbus=0:handle_sigfpe=0:"\ - "handle_sigill=0:print_stacktrace=0:"\ - "symbolize=0:symbolize_inline_frames=0" + os.environ['ASAN_OPTIONS'] = 'abort_on_error=1:detect_leaks=0:'\ + 'malloc_context_size=0:symbolize=0:'\ + 'allocator_may_return_null=1:'\ + 'detect_odr_violation=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_abort=0:'\ + 'handle_sigfpe=0:handle_sigill=0' + os.environ['UBSAN_OPTIONS'] = 'abort_on_error=1:'\ + 'allocator_release_to_os_interval_ms=500:'\ + 'handle_abort=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_sigfpe=0:'\ + 'handle_sigill=0:print_stacktrace=0:'\ + 'symbolize=0:symbolize_inline_frames=0' # Create at least one non-empty seed to start. utils.create_seed_file_for_empty_corpus(input_corpus) diff --git a/fuzzers/introspector_driven_focus/fuzzer.py b/fuzzers/introspector_driven_focus/fuzzer.py index d4cb1c7f9..cde79ba4b 100755 --- a/fuzzers/introspector_driven_focus/fuzzer.py +++ b/fuzzers/introspector_driven_focus/fuzzer.py @@ -28,7 +28,7 @@ def fuzz(input_corpus, output_corpus, target_binary): """Run fuzzer. Wrapper that uses the defaults when calling run_fuzzer.""" - with open('/focus_map.yaml', 'r') as focus_file: + with open('/focus_map.yaml', 'r', encoding='utf-8') as focus_file: focus_map = yaml.safe_load(focus_file) # This fuzzer just uses the first function from the list to focus benchmark = os.getenv('BENCHMARK', None) diff --git a/fuzzers/klee/fuzzer.py b/fuzzers/klee/fuzzer.py index 06c843dc7..2fc7b4d7d 100644 --- a/fuzzers/klee/fuzzer.py +++ b/fuzzers/klee/fuzzer.py @@ -104,7 +104,7 @@ def get_size_for_benchmark(): def get_bcs_for_shared_libs(fuzz_target): """Get shared libs paths for the fuzz_target""" - ldd_cmd = ['/usr/bin/ldd', '{target}'.format(target=fuzz_target)] + ldd_cmd = ['/usr/bin/ldd', f'{fuzz_target}'] output = '' try: output = subprocess.check_output(ldd_cmd, universal_newlines=True) @@ -115,23 +115,20 @@ def get_bcs_for_shared_libs(fuzz_target): if '=>' not in line: continue - out_dir = '{out}/{lib_bc_dir}'.format(out=os.environ['OUT'], - lib_bc_dir=LIB_BC_DIR) + out_dir = f'{os.environ["OUT"]}/{LIB_BC_DIR}' path = pathlib.Path(out_dir) path.mkdir(exist_ok=True) so_path = line.split('=>')[1].split(' ')[1] so_name = so_path.split('/')[-1].split('.')[0] if so_name: - getbc_cmd = 'extract-bc -o {out_dir}/{so_name}.bc {target}'.format( - target=so_path, out_dir=out_dir, so_name=so_name) - print('[extract-bc command] | {getbc_cmd}'.format( - getbc_cmd=getbc_cmd)) + getbc_cmd = f'extract-bc -o {out_dir}/{so_name}.bc {so_path}' + print(f'[extract-bc command] | {getbc_cmd}') # This will fail for most of the dependencies, which is fine. We # want to grab the .bc files for dependencies built in any given # benchmark's build.sh file. success = os.system(getbc_cmd) if success == 1: - print('Got a bc file for {target}'.format(target=so_path)) + print(f'Got a bc file for {so_path}') def get_bc_files(): @@ -222,13 +219,13 @@ def emptydir(path): def run(command, hide_output=False, ulimit_cmd=None): """Run the command |command|, optionally, run |ulimit_cmd| first.""" cmd = ' '.join(command) - print('[run_cmd] {}'.format(cmd)) + print(f'[run_cmd] {cmd}') output_stream = subprocess.DEVNULL if hide_output else None if ulimit_cmd: ulimit_command = [ulimit_cmd + ';'] ulimit_command.extend(command) - print('[ulimit_command] {}'.format(' '.join(ulimit_command))) + print(f'[ulimit_command] {" ".join(ulimit_command)}') ret = subprocess.call(' '.join(ulimit_command), stdout=output_stream, stderr=output_stream, @@ -238,8 +235,7 @@ def run(command, hide_output=False, ulimit_cmd=None): stdout=output_stream, stderr=output_stream) if ret != 0: - raise ValueError('command failed: {ret} - {cmd}'.format(ret=ret, - cmd=cmd)) + raise ValueError(f'command failed: {ret} - {cmd}') def convert_seed_inputs(ktest_tool, input_klee, input_corpus): @@ -268,25 +264,21 @@ def convert_seed_inputs(ktest_tool, input_klee, input_corpus): file_size = os.path.getsize(seedfile) benchmark_size = get_size_for_benchmark() if file_size > benchmark_size: - print('[run_fuzzer] Truncating {path} ({file_size}) to \ - {benchmark_size}'.format(path=seedfile, - file_size=file_size, - benchmark_size=benchmark_size)) + print(f'[run_fuzzer] Truncating {seedfile} ({file_size}) to ' + f'{benchmark_size}') os.truncate(seedfile, benchmark_size) - seed_in = '{seed}.ktest'.format(seed=seedfile) + seed_in = f'{seedfile}.ktest' seed_out = os.path.join(input_klee, os.path.basename(seed_in)) # Create file for symblic buffer - input_file = '{seed}.ktest.{symbolic}'.format(seed=seedfile, - symbolic=SYMBOLIC_BUFFER) - output_kfile = '{seed}.ktest'.format(seed=seedfile) + input_file = f'{seedfile}.ktest.{SYMBOLIC_BUFFER}' + output_kfile = f'{seedfile}.ktest' shutil.copyfile(seedfile, input_file) os.rename(seedfile, input_file) # Create file for mode version - model_input_file = '{seed}.ktest.{symbolic}'.format( - seed=seedfile, symbolic=MODEL_VERSION) + model_input_file = f'{seedfile}.ktest.{MODEL_VERSION}' with open(model_input_file, 'wb') as mfile: mfile.write(model) @@ -303,8 +295,7 @@ def convert_seed_inputs(ktest_tool, input_klee, input_corpus): n_converted += 1 - print('[run_fuzzer] Converted {converted} seed files'.format( - converted=n_converted)) + print(f'[run_fuzzer] Converted {n_converted} seed files') return n_converted @@ -322,12 +313,12 @@ def convert_individual_ktest(ktest_tool, kfile, queue_dir, output_klee, # And copy the resulting file in output_corpus ktest_fn = os.path.splitext(kfile)[0] - file_in = '{file}.{symbuf}'.format(file=kfile, symbuf=SYMBOLIC_BUFFER) + file_in = f'{kfile}.{SYMBOLIC_BUFFER}' file_out = os.path.join(queue_dir, os.path.basename(ktest_fn)) os.rename(file_in, file_out) # Check if this is a crash - crash_regex = os.path.join(output_klee, '{fn}.*.err'.format(fn=ktest_fn)) + crash_regex = os.path.join(output_klee, f'{ktest_fn}.*.err') crashes = glob.glob(crash_regex) n_crashes = 0 if len(crashes) == 1: @@ -350,14 +341,12 @@ def monitor_resource_usage(): start = datetime.now() while True: time.sleep(60 * 5) - message = '{cputimes}\n{virtmem}\n{swap}'.format( - cputimes=psutil.cpu_times_percent(percpu=False), - virtmem=psutil.virtual_memory(), - swap=psutil.swap_memory()) + message = (f'{psutil.cpu_times_percent(percpu=False)}\n' + f'{psutil.virtual_memory()}\n' + f'{psutil.swap_memory()}') now = datetime.now() print( - '[resource_thread] Resource usage after {time}:\n{message}'.format( - time=now - start, message=message)) + f'[resource_thread] Resource usage after {now - start}:\n{message}') # pylint: disable=import-error @@ -396,7 +385,7 @@ def fuzz(input_corpus, output_corpus, target_binary): print('[run_fuzzer] Running target with klee') klee_bin = os.path.join(out_dir, 'bin/klee') - target_binary_bc = '{}.bc'.format(target_binary) + target_binary_bc = f'{target_binary}.bc' max_time_seconds = ( int(os.getenv('MAX_TOTAL_TIME', str(MAX_TOTAL_TIME_DEFAULT))) * 4) // 5 @@ -405,8 +394,7 @@ def fuzz(input_corpus, output_corpus, target_binary): llvm_link_libs = [] for filename in get_bc_files(): - llvm_link_libs.append('-link-llvm-lib=./{lib_bc}/{filename}'.format( - lib_bc=LIB_BC_DIR, filename=filename)) + llvm_link_libs.append(f'-link-llvm-lib=./{LIB_BC_DIR}/{filename}') max_memory_mb = str(int(psutil.virtual_memory().available // 10**6 * 0.9)) diff --git a/fuzzers/lafintel/fuzzer.py b/fuzzers/lafintel/fuzzer.py index 29048d587..3cfc082c9 100644 --- a/fuzzers/lafintel/fuzzer.py +++ b/fuzzers/lafintel/fuzzer.py @@ -34,10 +34,10 @@ def remove_builtin(flag): return split[0] + '=' + ','.join(options) return flag - cflags = map(remove_builtin, os.environ["CFLAGS"].split()) - cxxflags = map(remove_builtin, os.environ["CXXFLAGS"].split()) - os.environ["CFLAGS"] = ' '.join(cflags) - os.environ["CXXFLAGS"] = ' '.join(cxxflags) + cflags = map(remove_builtin, os.environ['CFLAGS'].split()) + cxxflags = map(remove_builtin, os.environ['CXXFLAGS'].split()) + os.environ['CFLAGS'] = ' '.join(cflags) + os.environ['CXXFLAGS'] = ' '.join(cxxflags) # In php benchmark, there is a call to __builtin_cpu_supports("ssse3") # (see https://github.com/php/php-src/blob/master/Zend/zend_cpuinfo.h). # It is not supported by clang-3.8, so we define the MACRO below diff --git a/fuzzers/libafl/fuzzer.py b/fuzzers/libafl/fuzzer.py index f706d4aa0..63a4607ff 100755 --- a/fuzzers/libafl/fuzzer.py +++ b/fuzzers/libafl/fuzzer.py @@ -22,18 +22,18 @@ def prepare_fuzz_environment(input_corpus): """Prepare to fuzz with a LibAFL-based fuzzer.""" - os.environ['ASAN_OPTIONS'] = "abort_on_error=1:detect_leaks=0:"\ - "malloc_context_size=0:symbolize=0:"\ - "allocator_may_return_null=1:"\ - "detect_odr_violation=0:handle_segv=0:"\ - "handle_sigbus=0:handle_abort=0:"\ - "handle_sigfpe=0:handle_sigill=0" - os.environ['UBSAN_OPTIONS'] = "abort_on_error=1:"\ - "allocator_release_to_os_interval_ms=500:"\ - "handle_abort=0:handle_segv=0:"\ - "handle_sigbus=0:handle_sigfpe=0:"\ - "handle_sigill=0:print_stacktrace=0:"\ - "symbolize=0:symbolize_inline_frames=0" + os.environ['ASAN_OPTIONS'] = 'abort_on_error=1:detect_leaks=0:'\ + 'malloc_context_size=0:symbolize=0:'\ + 'allocator_may_return_null=1:'\ + 'detect_odr_violation=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_abort=0:'\ + 'handle_sigfpe=0:handle_sigill=0' + os.environ['UBSAN_OPTIONS'] = 'abort_on_error=1:'\ + 'allocator_release_to_os_interval_ms=500:'\ + 'handle_abort=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_sigfpe=0:'\ + 'handle_sigill=0:print_stacktrace=0:'\ + 'symbolize=0:symbolize_inline_frames=0' # Create at least one non-empty seed to start. utils.create_seed_file_for_empty_corpus(input_corpus) diff --git a/fuzzers/libfuzzer_dataflow_load/fuzzer.py b/fuzzers/libfuzzer_dataflow_load/fuzzer.py index 6acfbc905..f981e1cfc 100755 --- a/fuzzers/libfuzzer_dataflow_load/fuzzer.py +++ b/fuzzers/libfuzzer_dataflow_load/fuzzer.py @@ -21,7 +21,7 @@ def build(): """Build benchmark.""" - os.system("/src/fuzzers/libfuzzer_dataflow/variant-build.sh") + os.system('/src/fuzzers/libfuzzer_dataflow/variant-build.sh') # With LibFuzzer we use -fsanitize=fuzzer-no-link for build CFLAGS and then # /usr/lib/libFuzzer.a as the FUZZER_LIB for the main fuzzing binary. This diff --git a/fuzzers/libfuzzer_dataflow_store/fuzzer.py b/fuzzers/libfuzzer_dataflow_store/fuzzer.py index 48fbbf243..657c25912 100755 --- a/fuzzers/libfuzzer_dataflow_store/fuzzer.py +++ b/fuzzers/libfuzzer_dataflow_store/fuzzer.py @@ -21,7 +21,7 @@ def build(): """Build benchmark.""" - os.system("/src/fuzzers/libfuzzer_dataflow/variant-build.sh") + os.system('/src/fuzzers/libfuzzer_dataflow/variant-build.sh') # With LibFuzzer we use -fsanitize=fuzzer-no-link for build CFLAGS and then # /usr/lib/libFuzzer.a as the FUZZER_LIB for the main fuzzing binary. This diff --git a/fuzzers/nautilus/fuzzer.py b/fuzzers/nautilus/fuzzer.py index 34c0411a0..8cf7b4a0f 100755 --- a/fuzzers/nautilus/fuzzer.py +++ b/fuzzers/nautilus/fuzzer.py @@ -23,18 +23,18 @@ def prepare_fuzz_environment(input_corpus): """Prepare to fuzz with a LibAFL-based fuzzer.""" - os.environ['ASAN_OPTIONS'] = "abort_on_error=1:detect_leaks=0:"\ - "malloc_context_size=0:symbolize=0:"\ - "allocator_may_return_null=1:"\ - "detect_odr_violation=0:handle_segv=0:"\ - "handle_sigbus=0:handle_abort=0:"\ - "handle_sigfpe=0:handle_sigill=0" - os.environ['UBSAN_OPTIONS'] = "abort_on_error=1:"\ - "allocator_release_to_os_interval_ms=500:"\ - "handle_abort=0:handle_segv=0:"\ - "handle_sigbus=0:handle_sigfpe=0:"\ - "handle_sigill=0:print_stacktrace=0:"\ - "symbolize=0:symbolize_inline_frames=0" + os.environ['ASAN_OPTIONS'] = 'abort_on_error=1:detect_leaks=0:'\ + 'malloc_context_size=0:symbolize=0:'\ + 'allocator_may_return_null=1:'\ + 'detect_odr_violation=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_abort=0:'\ + 'handle_sigfpe=0:handle_sigill=0' + os.environ['UBSAN_OPTIONS'] = 'abort_on_error=1:'\ + 'allocator_release_to_os_interval_ms=500:'\ + 'handle_abort=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_sigfpe=0:'\ + 'handle_sigill=0:print_stacktrace=0:'\ + 'symbolize=0:symbolize_inline_frames=0' # Create at least one non-empty seed to start. utils.create_seed_file_for_empty_corpus(input_corpus) diff --git a/fuzzers/neuzz/fuzzer.py b/fuzzers/neuzz/fuzzer.py index 2cb5275d0..cd4eef3ae 100644 --- a/fuzzers/neuzz/fuzzer.py +++ b/fuzzers/neuzz/fuzzer.py @@ -53,10 +53,10 @@ def build(): def kill_afl(output_stream=subprocess.DEVNULL): """kill afl-fuzz process.""" - print("Warmed up!") + print('Warmed up!') # Can't avoid this because 'run_afl_fuzz' doesn't return a handle to # 'afl-fuzz' process so that we can kill it with subprocess.terminate() - subprocess.call(["pkill", "-f", "afl-fuzz"], + subprocess.call(['pkill', '-f', 'afl-fuzz'], stdout=output_stream, stderr=output_stream) @@ -73,9 +73,9 @@ def run_neuzz(input_corpus, afl.run_afl_fuzz(input_corpus, output_corpus, target_binary, additional_flags, hide_output) # After warming up, copy the 'queue' to use for neuzz input - print("[run_neuzz] Warmed up!") + print('[run_neuzz] Warmed up!') command = [ - "cp", "-RT", f"{output_corpus}/queue/", f"{input_corpus}_neuzzin/" + 'cp', '-RT', f'{output_corpus}/queue/', f'{input_corpus}_neuzzin/' ] print('[run_neuzz] Running command: ' + ' '.join(command)) @@ -88,22 +88,22 @@ def run_neuzz(input_corpus, # Spinning up the neural network command = [ - "python2", "./nn.py", '--output-folder', afl_output_dir, target_binary + 'python2', './nn.py', '--output-folder', afl_output_dir, target_binary ] print('[run_neuzz] Running command: ' + ' '.join(command)) - subprocess.Popen(command, stdout=output_stream, stderr=output_stream) + with subprocess.Popen(command, stdout=output_stream, stderr=output_stream): + pass time.sleep(40) target_rel_path = os.path.relpath(target_binary, os.getcwd()) # Spinning up neuzz command = [ - "./neuzz", "-m", "none", "-i", neuzz_input_dir, "-o", afl_output_dir, - target_rel_path, "@@" + './neuzz', '-m', 'none', '-i', neuzz_input_dir, '-o', afl_output_dir, + target_rel_path, '@@' ] print('[run_neuzz] Running command: ' + ' '.join(command)) - neuzz_proc = subprocess.Popen(command, - stdout=output_stream, - stderr=output_stream) - neuzz_proc.wait() + with subprocess.Popen(command, stdout=output_stream, + stderr=output_stream) as neuzz_proc: + neuzz_proc.wait() def fuzz(input_corpus, output_corpus, target_binary): diff --git a/fuzzers/pythia_bb/fuzzer.py b/fuzzers/pythia_bb/fuzzer.py index 853c0890c..7c4c44180 100755 --- a/fuzzers/pythia_bb/fuzzer.py +++ b/fuzzers/pythia_bb/fuzzer.py @@ -48,7 +48,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument """Gets fuzzer stats for AFL.""" # Get a dictionary containing the stats AFL reports. stats_file = os.path.join(output_corpus, 'fuzzer_stats') - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/pythia_effect_bb/fuzzer.py b/fuzzers/pythia_effect_bb/fuzzer.py index 853c0890c..7c4c44180 100755 --- a/fuzzers/pythia_effect_bb/fuzzer.py +++ b/fuzzers/pythia_effect_bb/fuzzer.py @@ -48,7 +48,7 @@ def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument """Gets fuzzer stats for AFL.""" # Get a dictionary containing the stats AFL reports. stats_file = os.path.join(output_corpus, 'fuzzer_stats') - with open(stats_file) as file_handle: + with open(stats_file, encoding='utf-8') as file_handle: stats_file_lines = file_handle.read().splitlines() stats_file_dict = {} for stats_line in stats_file_lines: diff --git a/fuzzers/symcc_afl/fuzzer.py b/fuzzers/symcc_afl/fuzzer.py index ff5f49e27..0c92eaa2c 100644 --- a/fuzzers/symcc_afl/fuzzer.py +++ b/fuzzers/symcc_afl/fuzzer.py @@ -30,7 +30,7 @@ def get_symcc_build_dir(target_directory): def build(): """Build an AFL version and SymCC version of the benchmark""" - print("Step 1: Building with AFL") + print('Step 1: Building with AFL') build_directory = os.environ['OUT'] # First build with AFL. @@ -42,46 +42,46 @@ def build(): # twice in the same directory without this. afl_fuzzer.build() - print("Step 2: Completed AFL build") + print('Step 2: Completed AFL build') # Copy over AFL artifacts needed by SymCC. - shutil.copy("/afl/afl-fuzz", build_directory) - shutil.copy("/afl/afl-showmap", build_directory) + shutil.copy('/afl/afl-fuzz', build_directory) + shutil.copy('/afl/afl-showmap', build_directory) # Build the SymCC-instrumented target. - print("Step 3: Building the benchmark with SymCC") + print('Step 3: Building the benchmark with SymCC') symcc_build_dir = get_symcc_build_dir(os.environ['OUT']) os.mkdir(symcc_build_dir) # Set flags to ensure compilation with SymCC. new_env = os.environ.copy() - new_env['CC'] = "/symcc/build/symcc" - new_env['CXX'] = "/symcc/build/sym++" - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CC'] = '/symcc/build/symcc' + new_env['CXX'] = '/symcc/build/sym++' + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_dir - new_env['CXXFLAGS'] += " -fno-sanitize=all " - new_env['CFLAGS'] += " -fno-sanitize=all " + new_env['CXXFLAGS'] += ' -fno-sanitize=all ' + new_env['CFLAGS'] += ' -fno-sanitize=all ' # Setting this environment variable instructs SymCC to use the # libcxx library compiled with SymCC instrumentation. - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' # Instructs SymCC to consider no symbolic inputs at runtime. This is needed # if, for example, some tests are run during compilation of the benchmark. - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' # Build benchmark. utils.build_benchmark(env=new_env) # Copy over symcc artifacts and symbolic libc++. shutil.copy( - "/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so", + '/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so', symcc_build_dir) - shutil.copy("/usr/lib/libz3.so", os.path.join(symcc_build_dir, "libz3.so")) - shutil.copy("/libcxx_native_build/lib/libc++.so.1", symcc_build_dir) - shutil.copy("/libcxx_native_build/lib/libc++abi.so.1", symcc_build_dir) - shutil.copy("/rust/bin/symcc_fuzzing_helper", symcc_build_dir) + shutil.copy('/usr/lib/libz3.so', os.path.join(symcc_build_dir, 'libz3.so')) + shutil.copy('/libcxx_native_build/lib/libc++.so.1', symcc_build_dir) + shutil.copy('/libcxx_native_build/lib/libc++abi.so.1', symcc_build_dir) + shutil.copy('/rust/bin/symcc_fuzzing_helper', symcc_build_dir) def launch_afl_thread(input_corpus, output_corpus, target_binary, @@ -109,25 +109,26 @@ def fuzz(input_corpus, output_corpus, target_binary, master_only=False): print('[run_fuzzer] Running AFL for SymCC') afl_fuzzer.prepare_fuzz_environment(input_corpus) launch_afl_thread(input_corpus, output_corpus, target_binary, - ["-M", "afl-master"]) + ['-M', 'afl-master']) time.sleep(5) if master_only: - sharing_dir = "afl-master" + sharing_dir = 'afl-master' else: launch_afl_thread(input_corpus, output_corpus, target_binary, - ["-S", "afl-secondary"]) + ['-S', 'afl-secondary']) time.sleep(5) - sharing_dir = "afl-secondary" + sharing_dir = 'afl-secondary' # Start an instance of SymCC. # We need to ensure it uses the symbolic version of libc++. - print("Starting the SymCC helper") + print('Starting the SymCC helper') new_environ = os.environ.copy() new_environ['LD_LIBRARY_PATH'] = symcc_workdir cmd = [ os.path.join(symcc_workdir, - "symcc_fuzzing_helper"), "-o", output_corpus, "-a", - sharing_dir, "-n", "symcc", "--", symcc_target_binary, "@@" + 'symcc_fuzzing_helper'), '-o', output_corpus, '-a', + sharing_dir, '-n', 'symcc', '--', symcc_target_binary, '@@' ] - subprocess.Popen(cmd, env=new_environ) + with subprocess.Popen(cmd, env=new_environ): + pass diff --git a/fuzzers/symcc_aflplusplus/fuzzer.py b/fuzzers/symcc_aflplusplus/fuzzer.py index 9a1b38da2..1737d0567 100644 --- a/fuzzers/symcc_aflplusplus/fuzzer.py +++ b/fuzzers/symcc_aflplusplus/fuzzer.py @@ -31,7 +31,7 @@ def get_symcc_build_dir(target_directory): def build(): """Build an AFL version and SymCC version of the benchmark""" - print("Step 1: Building with AFL") + print('Step 1: Building with AFL') build_directory = os.environ['OUT'] # Save the environment for use in SymCC @@ -44,48 +44,48 @@ def build(): # Restore SRC to its initial state so we can build again without any # trouble. For some OSS-Fuzz projects, build_benchmark cannot be run # twice in the same directory without this. - aflplusplus_fuzzer.build("tracepc") + aflplusplus_fuzzer.build('tracepc') - print("Step 2: Completed AFL build") + print('Step 2: Completed AFL build') # Copy over AFL artifacts needed by SymCC. - shutil.copy("/afl/afl-fuzz", build_directory) - shutil.copy("/afl/afl-showmap", build_directory) + shutil.copy('/afl/afl-fuzz', build_directory) + shutil.copy('/afl/afl-showmap', build_directory) # Build the SymCC-instrumented target. - print("Step 3: Building the benchmark with SymCC") + print('Step 3: Building the benchmark with SymCC') symcc_build_dir = get_symcc_build_dir(os.environ['OUT']) os.mkdir(symcc_build_dir) # Set flags to ensure compilation with SymCC. - new_env['CC'] = "/symcc/build/symcc" - new_env['CXX'] = "/symcc/build/sym++" - new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace("-stlib=libc++", "") + new_env['CC'] = '/symcc/build/symcc' + new_env['CXX'] = '/symcc/build/sym++' + new_env['CXXFLAGS'] = new_env['CXXFLAGS'].replace('-stlib=libc++', '') new_env['CXXFLAGS'] += ' -ldl' new_env['FUZZER_LIB'] = '/libfuzzer-harness.o' new_env['OUT'] = symcc_build_dir - new_env['CXXFLAGS'] += " -fno-sanitize=all " - new_env['CFLAGS'] += " -fno-sanitize=all " + new_env['CXXFLAGS'] += ' -fno-sanitize=all ' + new_env['CFLAGS'] += ' -fno-sanitize=all ' # Setting this environment variable instructs SymCC to use the # libcxx library compiled with SymCC instrumentation. - new_env['SYMCC_LIBCXX_PATH'] = "/libcxx_native_build" + new_env['SYMCC_LIBCXX_PATH'] = '/libcxx_native_build' # Instructs SymCC to consider no symbolic inputs at runtime. This is needed # if, for example, some tests are run during compilation of the benchmark. - new_env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" + new_env['SYMCC_NO_SYMBOLIC_INPUT'] = '1' # Build benchmark. utils.build_benchmark(env=new_env) # Copy over symcc artifacts and symbolic libc++. shutil.copy( - "/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so", + '/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so', symcc_build_dir) - shutil.copy("/usr/lib/libz3.so", os.path.join(symcc_build_dir, "libz3.so")) - shutil.copy("/libcxx_native_build/lib/libc++.so.1", symcc_build_dir) - shutil.copy("/libcxx_native_build/lib/libc++abi.so.1", symcc_build_dir) - shutil.copy("/rust/bin/symcc_fuzzing_helper", symcc_build_dir) + shutil.copy('/usr/lib/libz3.so', os.path.join(symcc_build_dir, 'libz3.so')) + shutil.copy('/libcxx_native_build/lib/libc++.so.1', symcc_build_dir) + shutil.copy('/libcxx_native_build/lib/libc++abi.so.1', symcc_build_dir) + shutil.copy('/rust/bin/symcc_fuzzing_helper', symcc_build_dir) def launch_afl_thread(input_corpus, output_corpus, target_binary, @@ -108,26 +108,27 @@ def fuzz(input_corpus, output_corpus, target_binary): target_binary_name = os.path.basename(target_binary) symcc_target_binary = os.path.join(symcc_workdir, target_binary_name) - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # Start a master and secondary instance of AFL. # We need both because of the way SymCC works. print('[run_fuzzer] Running AFL for SymCC') afl_fuzzer.prepare_fuzz_environment(input_corpus) - launch_afl_thread(input_corpus, output_corpus, target_binary, ["-S", "afl"]) + launch_afl_thread(input_corpus, output_corpus, target_binary, ['-S', 'afl']) time.sleep(5) launch_afl_thread(input_corpus, output_corpus, target_binary, - ["-S", "afl-secondary"]) + ['-S', 'afl-secondary']) time.sleep(5) # Start an instance of SymCC. # We need to ensure it uses the symbolic version of libc++. - print("Starting the SymCC helper") + print('Starting the SymCC helper') new_environ = os.environ.copy() new_environ['LD_LIBRARY_PATH'] = symcc_workdir cmd = [ os.path.join(symcc_workdir, - "symcc_fuzzing_helper"), "-o", output_corpus, "-a", - "afl-secondary", "-n", "symcc", "-m", "--", symcc_target_binary, "@@" + 'symcc_fuzzing_helper'), '-o', output_corpus, '-a', + 'afl-secondary', '-n', 'symcc', '-m', '--', symcc_target_binary, '@@' ] - subprocess.Popen(cmd, env=new_environ) + with subprocess.Popen(cmd, env=new_environ): + pass diff --git a/fuzzers/symcc_aflplusplus_single/fuzzer.py b/fuzzers/symcc_aflplusplus_single/fuzzer.py index 1bea1a42d..15b4cfd02 100644 --- a/fuzzers/symcc_aflplusplus_single/fuzzer.py +++ b/fuzzers/symcc_aflplusplus_single/fuzzer.py @@ -31,7 +31,7 @@ def get_symcc_build_dir(target_directory): def build(): """Build an AFL version and SymCC version of the benchmark""" - print("Step 1: Building with AFL and SymCC") + print('Step 1: Building with AFL and SymCC') build_directory = os.environ['OUT'] # First build with AFL. @@ -41,23 +41,23 @@ def build(): # Restore SRC to its initial state so we can build again without any # trouble. For some OSS-Fuzz projects, build_benchmark cannot be run # twice in the same directory without this. - aflplusplus_fuzzer.build("tracepc", "symcc") + aflplusplus_fuzzer.build('tracepc', 'symcc') - print("Step 2: Completed AFL build") + print('Step 2: Completed AFL build') # Copy over AFL artifacts needed by SymCC. - shutil.copy("/afl/afl-fuzz", build_directory) - shutil.copy("/afl/afl-showmap", build_directory) + shutil.copy('/afl/afl-fuzz', build_directory) + shutil.copy('/afl/afl-showmap', build_directory) # Copy over symcc artifacts and symbolic libc++. - print("Step 3: Copying SymCC files") + print('Step 3: Copying SymCC files') symcc_build_dir = get_symcc_build_dir(os.environ['OUT']) shutil.copy( - "/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so", + '/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so', symcc_build_dir) - shutil.copy("/usr/lib/libz3.so", os.path.join(symcc_build_dir, "libz3.so")) - shutil.copy("/libcxx_native_build/lib/libc++.so.1", symcc_build_dir) - shutil.copy("/libcxx_native_build/lib/libc++abi.so.1", symcc_build_dir) - shutil.copy("/rust/bin/symcc_fuzzing_helper", symcc_build_dir) + shutil.copy('/usr/lib/libz3.so', os.path.join(symcc_build_dir, 'libz3.so')) + shutil.copy('/libcxx_native_build/lib/libc++.so.1', symcc_build_dir) + shutil.copy('/libcxx_native_build/lib/libc++abi.so.1', symcc_build_dir) + shutil.copy('/rust/bin/symcc_fuzzing_helper', symcc_build_dir) def launch_afl_thread(input_corpus, output_corpus, target_binary, @@ -80,24 +80,25 @@ def fuzz(input_corpus, output_corpus, target_binary): target_binary_name = os.path.basename(target_binary) symcc_target_binary = os.path.join(symcc_workdir, target_binary_name) - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # Start a master and secondary instance of AFL. # We need both because of the way SymCC works. print('[run_fuzzer] Running AFL for SymCC') afl_fuzzer.prepare_fuzz_environment(input_corpus) launch_afl_thread(input_corpus, output_corpus, target_binary, - ["-S", "afl-secondary"]) + ['-S', 'afl-secondary']) time.sleep(5) # Start an instance of SymCC. # We need to ensure it uses the symbolic version of libc++. - print("Starting the SymCC helper") + print('Starting the SymCC helper') new_environ = os.environ.copy() new_environ['LD_LIBRARY_PATH'] = symcc_workdir cmd = [ os.path.join(symcc_workdir, - "symcc_fuzzing_helper"), "-o", output_corpus, "-a", - "afl-secondary", "-n", "symcc", "-m", "--", symcc_target_binary, "@@" + 'symcc_fuzzing_helper'), '-o', output_corpus, '-a', + 'afl-secondary', '-n', 'symcc', '-m', '--', symcc_target_binary, '@@' ] - subprocess.Popen(cmd, env=new_environ) + with subprocess.Popen(cmd, env=new_environ): + pass diff --git a/fuzzers/symqemu_aflplusplus/fuzzer.py b/fuzzers/symqemu_aflplusplus/fuzzer.py index 47348dcf6..bb8e1c0ec 100644 --- a/fuzzers/symqemu_aflplusplus/fuzzer.py +++ b/fuzzers/symqemu_aflplusplus/fuzzer.py @@ -39,7 +39,7 @@ def build(): fuzz_target = os.getenv('FUZZ_TARGET') # First, build an uninstrumented binary for Eclipser. - aflplusplus_fuzzer.build("qemu", "eclipser") + aflplusplus_fuzzer.build('qemu', 'eclipser') eclipser_dir = get_symcc_build_dir(build_directory) os.mkdir(eclipser_dir) fuzz_binary = build_directory + '/' + fuzz_target @@ -49,23 +49,23 @@ def build(): # Second, build an instrumented binary for AFL++. os.environ = orig_env - aflplusplus_fuzzer.build("tracepc") + aflplusplus_fuzzer.build('tracepc') print('[build] Copying afl-fuzz to $OUT directory') # Copy afl-fuzz shutil.copy('/afl/afl-fuzz', build_directory) - shutil.copy("/afl/afl-showmap", build_directory) - shutil.copy("/rust/bin/symcc_fuzzing_helper", eclipser_dir) + shutil.copy('/afl/afl-showmap', build_directory) + shutil.copy('/rust/bin/symcc_fuzzing_helper', eclipser_dir) symcc_build_dir = get_symcc_build_dir(os.environ['OUT']) # Copy over symcc artifacts and symbolic libc++. shutil.copy( - "/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so", + '/symcc/build//SymRuntime-prefix/src/SymRuntime-build/libSymRuntime.so', symcc_build_dir) - shutil.copy("/usr/lib/libz3.so", os.path.join(symcc_build_dir, "libz3.so")) - shutil.copy("/rust/bin/symcc_fuzzing_helper", symcc_build_dir) - shutil.copy("/symqemu/build/x86_64-linux-user/symqemu-x86_64", + shutil.copy('/usr/lib/libz3.so', os.path.join(symcc_build_dir, 'libz3.so')) + shutil.copy('/rust/bin/symcc_fuzzing_helper', symcc_build_dir) + shutil.copy('/symqemu/build/x86_64-linux-user/symqemu-x86_64', symcc_build_dir) @@ -89,31 +89,32 @@ def fuzz(input_corpus, output_corpus, target_binary): target_binary_name = os.path.basename(target_binary) symcc_target_binary = os.path.join(symcc_workdir, target_binary_name) - os.environ['AFL_DISABLE_TRIM'] = "1" + os.environ['AFL_DISABLE_TRIM'] = '1' # Start a master and secondary instance of AFL. # We need both because of the way SymCC works. print('[run_fuzzer] Running AFL for SymCC') afl_fuzzer.prepare_fuzz_environment(input_corpus) launch_afl_thread(input_corpus, output_corpus, target_binary, - ["-S", "afl-secondary"]) + ['-S', 'afl-secondary']) time.sleep(5) # Start an instance of SymCC. # We need to ensure it uses the symbolic version of libc++. - symqemu_target = os.path.join(symcc_workdir, "symqemu-x86_64") + symqemu_target = os.path.join(symcc_workdir, 'symqemu-x86_64') if os.path.isfile(symqemu_target): - print("Found symqemu target") + print('Found symqemu target') else: - print("Did not find symqemu target") + print('Did not find symqemu target') - print("Starting the SymCC helper") + print('Starting the SymCC helper') new_environ = os.environ.copy() new_environ['LD_LIBRARY_PATH'] = symcc_workdir cmd = [ - os.path.join(symcc_workdir, "symcc_fuzzing_helper"), "-o", - output_corpus, "-a", "afl-secondary", "-n", "symqemu", "-m", "--", - symqemu_target, symcc_target_binary, "@@" + os.path.join(symcc_workdir, 'symcc_fuzzing_helper'), '-o', + output_corpus, '-a', 'afl-secondary', '-n', 'symqemu', '-m', '--', + symqemu_target, symcc_target_binary, '@@' ] - print("Running command: %s" % (" ".join(cmd))) - subprocess.Popen(cmd, env=new_environ) + print(f'Running command: {" ".join(cmd)}') + with subprocess.Popen(cmd, env=new_environ): + pass diff --git a/fuzzers/test_fuzzers.py b/fuzzers/test_fuzzers.py index c67a337c8..7e927b367 100644 --- a/fuzzers/test_fuzzers.py +++ b/fuzzers/test_fuzzers.py @@ -39,7 +39,7 @@ def get_all_fuzzer_dirs(): def _get_fuzzer_module(fuzzer): """Get the module for |fuzzer|'s fuzzer.py.""" - return 'fuzzers.{}.fuzzer'.format(fuzzer) + return f'fuzzers.{fuzzer}.fuzzer' def _get_all_fuzzer_modules(): diff --git a/fuzzers/token_level/fuzzer.py b/fuzzers/token_level/fuzzer.py index d85de4da0..8a9023aa4 100755 --- a/fuzzers/token_level/fuzzer.py +++ b/fuzzers/token_level/fuzzer.py @@ -23,18 +23,18 @@ def prepare_fuzz_environment(input_corpus): """Prepare to fuzz with a LibAFL-based fuzzer.""" - os.environ['ASAN_OPTIONS'] = "abort_on_error=1:detect_leaks=0:"\ - "malloc_context_size=0:symbolize=0:"\ - "allocator_may_return_null=1:"\ - "detect_odr_violation=0:handle_segv=0:"\ - "handle_sigbus=0:handle_abort=0:"\ - "handle_sigfpe=0:handle_sigill=0" - os.environ['UBSAN_OPTIONS'] = "abort_on_error=1:"\ - "allocator_release_to_os_interval_ms=500:"\ - "handle_abort=0:handle_segv=0:"\ - "handle_sigbus=0:handle_sigfpe=0:"\ - "handle_sigill=0:print_stacktrace=0:"\ - "symbolize=0:symbolize_inline_frames=0" + os.environ['ASAN_OPTIONS'] = 'abort_on_error=1:detect_leaks=0:'\ + 'malloc_context_size=0:symbolize=0:'\ + 'allocator_may_return_null=1:'\ + 'detect_odr_violation=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_abort=0:'\ + 'handle_sigfpe=0:handle_sigill=0' + os.environ['UBSAN_OPTIONS'] = 'abort_on_error=1:'\ + 'allocator_release_to_os_interval_ms=500:'\ + 'handle_abort=0:handle_segv=0:'\ + 'handle_sigbus=0:handle_sigfpe=0:'\ + 'handle_sigill=0:print_stacktrace=0:'\ + 'symbolize=0:symbolize_inline_frames=0' # Create at least one non-empty seed to start. utils.create_seed_file_for_empty_corpus(input_corpus) diff --git a/fuzzers/utils.py b/fuzzers/utils.py index 0a2bca0e6..c44169c37 100644 --- a/fuzzers/utils.py +++ b/fuzzers/utils.py @@ -33,8 +33,8 @@ # Flags to use when using sanitizer for bug based benchmarking. SANITIZER_FLAGS = [ '-fsanitize=address', - # Matches UBSan features enabled in OSS-Fuzz. - # See https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/Dockerfile#L94 + # Matches UBSan features enabled in OSS-Fuzz. See + # https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/Dockerfile#L94 '-fsanitize=array-bounds,bool,builtin,enum,float-divide-by-zero,function,' 'integer-divide-by-zero,null,object-size,return,returns-nonnull-attribute,' 'shift,signed-integer-overflow,unreachable,vla-bound,vptr', @@ -77,8 +77,7 @@ def build_benchmark(env=None): benchmark = os.getenv('BENCHMARK') fuzzer = os.getenv('FUZZER') - print('Building benchmark {benchmark} with fuzzer {fuzzer}'.format( - benchmark=benchmark, fuzzer=fuzzer)) + print(f'Building benchmark {benchmark} with fuzzer {fuzzer}') subprocess.check_call(['/bin/bash', '-ex', build_script], env=env) @@ -96,7 +95,7 @@ def append_flags(env_var, additional_flags, env=None): def get_config_value(attribute): """Gets config attribute value from benchmark config yaml file.""" - with open(BENCHMARK_CONFIG_YAML_PATH) as file_handle: + with open(BENCHMARK_CONFIG_YAML_PATH, encoding='utf-8') as file_handle: config = yaml.load(file_handle, yaml.SafeLoader) return config.get(attribute) @@ -150,7 +149,7 @@ def get_dictionary_path(target_binary): return None config = configparser.ConfigParser() - with open(options_file_path, 'r') as file_handle: + with open(options_file_path, 'r', encoding='utf-8') as file_handle: try: config.read_file(file_handle) except configparser.Error as error: @@ -211,8 +210,7 @@ def initialize_env(env=None): set_compilation_flags(env) for env_var in ['FUZZ_TARGET', 'CFLAGS', 'CXXFLAGS']: - print('{env_var} = {env_value}'.format(env_var=env_var, - env_value=os.getenv(env_var))) + print(f'{env_var} = {os.getenv(env_var)}') def get_env(env_var, default_value=None): @@ -240,5 +238,5 @@ def create_seed_file_for_empty_corpus(input_corpus): print('Creating a fake seed file in empty corpus directory.') default_seed_file = os.path.join(input_corpus, 'default_seed') - with open(default_seed_file, 'w') as file_handle: + with open(default_seed_file, 'w', encoding='utf-8') as file_handle: file_handle.write('hi') diff --git a/fuzzers/weizz_qemu/fuzzer.py b/fuzzers/weizz_qemu/fuzzer.py index 215c611fe..739ec3e1b 100644 --- a/fuzzers/weizz_qemu/fuzzer.py +++ b/fuzzers/weizz_qemu/fuzzer.py @@ -27,11 +27,11 @@ def build(): os.environ['FUZZER_LIB'] = '/libQEMU.a' # QEMU doesn't like ASan cflags = filter(lambda flag: not flag.startswith('-fsanitize=address'), - os.environ["CFLAGS"].split()) + os.environ['CFLAGS'].split()) cxxflags = filter(lambda flag: not flag.startswith('-fsanitize=address'), - os.environ["CXXFLAGS"].split()) - os.environ["CFLAGS"] = ' '.join(cflags) - os.environ["CXXFLAGS"] = ' '.join(cxxflags) + os.environ['CXXFLAGS'].split()) + os.environ['CFLAGS'] = ' '.join(cflags) + os.environ['CXXFLAGS'] = ' '.join(cxxflags) utils.build_benchmark() diff --git a/presubmit.py b/presubmit.py index f97f9ed92..7f99b0096 100644 --- a/presubmit.py +++ b/presubmit.py @@ -283,8 +283,8 @@ def validate_experiment_requests(paths: List[Path]): experiment_requests = yaml_utils.read( automatic_run_experiment.REQUESTED_EXPERIMENTS_PATH) except yaml.parser.ParserError: - print('Error parsing %s.' % - automatic_run_experiment.REQUESTED_EXPERIMENTS_PATH) + print('Error parsing ' + f'{automatic_run_experiment.REQUESTED_EXPERIMENTS_PATH}.') return False # Only validate the latest request. @@ -292,8 +292,8 @@ def validate_experiment_requests(paths: List[Path]): experiment_requests[:1]) if not result: - print('%s is not valid.' % - automatic_run_experiment.REQUESTED_EXPERIMENTS_PATH) + print(f'{automatic_run_experiment.REQUESTED_EXPERIMENTS_PATH}' + 'is not valid.') return result @@ -329,9 +329,9 @@ def license_check(paths: List[Path]) -> bool: if is_path_ignored(path): continue - with open(path) as file_handle: + with open(path, encoding='utf-8') as file_handle: if _LICENSE_CHECK_STRING not in file_handle.read(): - print('Missing license header in file %s.' % str(path)) + print(f'Missing license header in file {str(path)}.') success = False return success @@ -375,11 +375,11 @@ def do_default_checks(file_paths: List[Path], checks) -> bool: continue if not check(file_paths): - print('ERROR: %s failed, see errors above.' % check_name) + print(f'ERROR: {check_name} failed, see errors above.') failed_checks.append(check_name) if failed_checks: - print('Failed checks: %s' % ' '.join(failed_checks)) + print(f'Failed checks: {" ".join(failed_checks)}') return False return True @@ -440,7 +440,7 @@ def do_single_check(command: str, relevant_files: List[Path], else: success = check(relevant_files) if not success: - print('ERROR: %s failed, see errors above.' % check.__name__) + print(f'ERROR: {check.__name__} failed, see errors above.') return success diff --git a/src_analysis/diff_utils.py b/src_analysis/diff_utils.py index a05bc051f..476e0baf2 100644 --- a/src_analysis/diff_utils.py +++ b/src_analysis/diff_utils.py @@ -50,10 +50,9 @@ def get_changed_files(commit_name: str = 'origin...') -> List[str]: # This probably won't happen to anyone. It can happen if your copy # of the repo wasn't cloned so give instructions on how to handle. pass - raise DiffError(( - '"%s" failed.\n' + raise DiffError( + f'"{" ".join(committed_diff_command)}" failed.\n' 'Please run "git fetch origin master --unshallow && ' 'git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master" ' 'and try again.\n' - 'Please file an issue if this doesn\'t fix things.') % - ' '.join(committed_diff_command)) + 'Please file an issue if this doesn\'t fix things.') diff --git a/src_analysis/fuzzer_dependencies.py b/src_analysis/fuzzer_dependencies.py index 7cf6fb9f9..e8dcf0f36 100644 --- a/src_analysis/fuzzer_dependencies.py +++ b/src_analysis/fuzzer_dependencies.py @@ -48,7 +48,7 @@ def _get_fuzzer_module_name(fuzzer: str) -> str: """Returns the name of the fuzzer.py module of |fuzzer|. Assumes |fuzzer| is an underlying fuzzer.""" - return 'fuzzers.{}.fuzzer'.format(fuzzer) + return f'fuzzers.{fuzzer}.fuzzer' def is_builtin_module(module: types.ModuleType) -> bool: diff --git a/src_analysis/test_benchmark_dependencies.py b/src_analysis/test_benchmark_dependencies.py index 21e08a861..0ef27e810 100644 --- a/src_analysis/test_benchmark_dependencies.py +++ b/src_analysis/test_benchmark_dependencies.py @@ -52,4 +52,4 @@ def test_get_files_dependent_benchmarks(): benchmark_dependencies.get_files_dependent_benchmarks( [fake_build_sh_path])) - assert dependent_benchmarks == [] + assert not dependent_benchmarks