Skip to main content

Fuzz test Python modules with libFuzzer.

Project description

buildstatus

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for pyfuzzer, version 0.10.0
Filename, size File type Python version Upload date Hashes
Filename, size pyfuzzer-0.10.0.tar.gz (8.3 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring DigiCert DigiCert EV certificate Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page