Skip to main content

Fuzz test Python modules with libFuzzer.

Project description

buildstatus coverage

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.generic when testing the module hello_world.

$ cd examples/hello_world
$ pyfuzzer run -l max_total_time=1 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
corpus/25409981b15b978c9fb5a5a2f4dab0c4b04e295f:
    tell(b'') = 5
corpus/a8a4e6c9abfd3c6cba171579190702ddc1317df0:
    tell(b'\xfd#') = b'Hello!'
corpus/80f87702ef9fbe4baf17095c79ff928b9fa1ea14:
    tell(b'\x00') = True
corpus/be3d1b7df189727b2cecd6526aa8f24abbf6df10:
    tell(b'\x00\xfd\x00') = 0
corpus/defd8787d638f271cd83362eafe7fdeed9fa4a8f:
    tell(None) raises:
    Traceback (most recent call last):
      File "/home/erik/workspace/pyfuzzer/pyfuzzer/mutators/utils.py", line 35, in print_callable
        res = obj(*args)
    TypeError: expected bytes, NoneType found

See the hello_world for all files.

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.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
crash-1013ed88cd71fd14407b2bdbc17b95d7bc317c21:
    tell(b'\n\xbf+') = None

See the hello_world_fatal_error for all files.

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 -l max_total_time=1 -m hello_world_mutator.py hello_world.c
...

See the hello_world_custom_mutator for all files.

Mutators

A mutator module uses data from libFuzzer to test a module. A mutator module must implement the function setup(module), where module is the module under test. It shall return a mutator instance that implements the methods test_one_input(self, data) and test_one_input_print(self, data), where data is the data generated by libFuzzer (as a bytes object).

test_one_input(self, data) performs the actual fuzz testing, while test_one_input_print(self, 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.

from pyfuzzer.mutators.generic import print_callable

class Mutator:

    def __init__(self, module):
        self._module = module

    def test_one_input(self, data):
        return module.crc_32(data)

    def test_one_input_print(self, data):
        print_callable(self._module.crc_32, [data])

 def setup(module):
     return Mutator(module)

Ideas

  • Add support to fuzz test pure Python modules by generating C code using Cython.

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.19.0
Filename, size File type Python version Upload date Hashes
Filename, size pyfuzzer-0.19.0.tar.gz (12.4 kB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page