Fuzz test Python modules with libFuzzer.
Project description
About
Use libFuzzer to fuzz test Python 3.6+ C extension modules.
Installation
clang 8 or later is required.
$ apt install clang
$ pip install pyfuzzer
Example Usage
Hello world
Use the default mutator pyfuzzer.mutators.random when testing the module hello_world.
$ cd examples/hello_world
$ pyfuzzer run hello_world hello_world.c
<lots of libFuzzer output>
Print the function calls that found new code paths. This information is usually useful when writing unit tests.
$ pyfuzzer print_corpus
tell(b'') = 5
tell(b'@') = True
tell(None) raises:
Traceback (most recent call last):
File "/home/erik/workspace/pyfuzzer/pyfuzzer/mutators/utils.py", line 18, in print_function
res = func(*args)
TypeError: expected bytes, NoneType found
tell(b'@\x01\x00') = 0
tell(b'#@') = b'Hello!'
See the hello_world for all files and full output.
Hello world fatal error
Similar to the previous example, but triggers a fatal error when tell() is called with a bytes object longer than 2 bytes as its first argument.
$ cd examples/hello_world_fatal_error
$ pyfuzzer run hello_world hello_world.c
...
Fatal Python error: deallocating None
Current thread 0x00007f7ca99c2780 (most recent call first):
...
Print the function call that caused the crash. Just as expected, the first argument is clearly longer than 2 bytes.
$ pyfuzzer print_crashes
tell(b'..............................................') = None
See the hello_world_fatal_error for all files and full output.
Custom mutator
Use the custom mutator hello_world_mutator when testing the module hello_world.
Testing with a custom mutator is often more efficient than using a generic one.
$ cd examples/hello_world_custom_mutator
$ pyfuzzer run -m hello_world_mutator.py hello_world hello_world.c
...
See the hello_world_custom_mutator for all files and full output.
Mutators
A mutator uses data from libFuzzer to test a module. A mutator must implement the functions test_one_input(module, data) and test_one_input_print(module, data), where module is the module under test and data is the data generated by libFuzzer (as a bytes object).
test_one_input(module, data) performs the actual fuzz testing, while test_one_input_print(module, data) prints corpus and crashes.
A minimal mutator fuzz testing a CRC-32 algorithm could look like below. It simply calls crc_32() with data as its only argument.
import traceback
from .pyfuzzer.mutators.utils import print_function
def test_one_input(module, data):
module.crc_32(data)
def test_one_input_print(module, data):
print_function(module.crc_32, [data])
Ideas
Use type annotations for less type errors in generic mutator.
Add support to fuzz test pure Python modules by generating C code using Cython.
Call random methods on classes.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.