python-afl-0.6.1/ 0000755 0000000 0000000 00000000000 13136647265 013612 5 ustar 00root root 0000000 0000000 python-afl-0.6.1/py-afl-tmin 0000755 0000000 0000000 00000000411 13065510072 015653 0 ustar 00root root 0000000 0000000 #!/bin/sh
export PYTHON_AFL_SIGNAL=${PYTHON_AFL_SIGNAL:-SIGUSR1}
if ! command -v afl-tmin > /dev/null
then
cat >&2 < installed?
EOF
exit 127
fi
exec afl-tmin "$@"
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/MANIFEST.in 0000644 0000000 0000000 00000000233 13071147120 015325 0 ustar 00root root 0000000 0000000 exclude *.c
exclude README
include *.pyx
include LICENSE
include MANIFEST.in
include doc/*
include private/*
include py-afl-*
recursive-include tests *.py
python-afl-0.6.1/doc/ 0000755 0000000 0000000 00000000000 13136647265 014357 5 ustar 00root root 0000000 0000000 python-afl-0.6.1/doc/LICENSE 0000644 0000000 0000000 00000002074 13066016716 015360 0 ustar 00root root 0000000 0000000 Copyright © 2013-2017 Jakub Wilk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
python-afl-0.6.1/doc/README 0000644 0000000 0000000 00000005204 13070751117 015225 0 ustar 00root root 0000000 0000000 This is experimental module that enables
`American fuzzy lop`_ fork server and instrumentation for pure-Python code.
.. _American fuzzy lop: http://lcamtuf.coredump.cx/afl/
HOWTO
-----
* Add this code (ideally, after all other modules are already imported) to
the target program:
.. code:: python
import afl
afl.init()
* The instrumentation is currently implemented with a `trace function`_,
which is called whenever a new local scope is entered.
You might need to wrap the code of the main program in a function
to get it instrumented correctly.
.. _trace function:
https://docs.python.org/2/library/sys.html#sys.settrace
* Optionally, add this code at the end of the target program:
.. code:: python
os._exit(0)
This should speed up fuzzing considerably,
at the risk of not catching bugs that could happen during normal exit.
* For persistent mode, wrap the tested code in this loop:
.. code:: python
while afl.loop(N):
...
where ``N`` is the number of inputs to process before restarting.
You shouldn't call ``afl.init()`` in this case.
afl-fuzz ≥ 1.82b is required for this feature.
* Use *py-afl-fuzz* instead of *afl-fuzz*::
$ py-afl-fuzz [options] -- /path/to/fuzzed/python/script [...]
* The instrumentation is a bit slow at the moment,
so you might want to enable the dumb mode (``-n``),
while still leveraging the fork server.
afl-fuzz ≥ 1.95b is required for this feature.
Environment variables
---------------------
The following environment variables affect *python-afl* behavior:
``PYTHON_AFL_SIGNAL``
If this variable is set, *python-afl* installs an exception hook
that kills the current process with the selected signal.
That way *afl-fuzz* can treat unhandled exceptions as crashes.
By default, *py-afl-fuzz*, *py-afl-showmap*, *python-afl-cmin*,
and *py-afl-tmin* set this variable to ``SIGUSR1``.
You can set ``PYTHON_AFL_SIGNAL`` to another signal;
or set it to ``0`` to disable the exception hook.
``PYTHON_AFL_PERSISTENT``
Persistent mode is enabled only if this variable is set.
*py-afl-fuzz* sets this variable automatically,
so there should normally no need to set it manually.
Further reading
---------------
* `Introduction to Fuzzing in Python with AFL `_ by Alex Gaynor
* `AFL's README `_
Prerequisites
-------------
To build the module, you will need:
* Python 2.6+ or 3.2+
* Cython ≥ 0.19 (only at build time)
*py-afl-fuzz* requires AFL proper to be installed.
.. vim:ft=rst ts=3 sts=3 sw=3 et
python-afl-0.6.1/doc/changelog 0000644 0000000 0000000 00000010624 13136646574 016236 0 ustar 00root root 0000000 0000000 python-afl (0.6.1) unstable; urgency=low
* Improve the test suite.
+ Make the py-afl-cmin test pass when run in a subdirectory of /tmp.
-- Jakub Wilk Fri, 28 Jul 2017 16:43:06 +0200
python-afl (0.6) unstable; urgency=low
* Add py-afl-cmin.
Thanks to @jabdoa2 for the bug report.
https://github.com/jwilk/python-afl/issues/6
* Add py-afl-showmap and py-afl-tmin.
Bare afl-showmap and afl-tmin were broken since 0.5.
* Put license into a separate file.
* Improve the test suite.
* Update URLs in trophy-case.
-- Jakub Wilk Wed, 05 Apr 2017 13:28:37 +0200
python-afl (0.5.5) unstable; urgency=low
* Improve the test suite:
+ Kill stray processes that afl-fuzz might have left behind.
Thanks to Daniel Stender for the bug report.
https://bugs.debian.org/833675
https://github.com/jwilk/python-afl/issues/4
-- Jakub Wilk Tue, 16 Aug 2016 22:08:57 +0200
python-afl (0.5.4) unstable; urgency=low
* Improve README:
+ Fix formatting.
+ Add “Further reading” links.
+ Document runtime and build-time dependencies.
* Improve error handling.
Thanks to @PhillipSz for the bug report.
https://github.com/jwilk/python-afl/issues/1
* Improve the setup script:
+ Make the package installable with pip, even when Cython were missing.
Thanks to @mrmagooey for the bug report.
https://github.com/jwilk/python-afl/issues/3
+ Add “Programming Language” classifiers.
* Improve the test suite.
-- Jakub Wilk Sat, 30 Jul 2016 16:43:52 +0200
python-afl (0.5.3) unstable; urgency=low
* Fix compatibility with Cython 0.24.
-- Jakub Wilk Thu, 07 Apr 2016 12:56:08 +0200
python-afl (0.5.2) unstable; urgency=low
[ Jakub Wilk ]
* Document that afl-fuzz ≥ 1.95b is required for the -n option.
* Document that you might need to wrap code in a function to get it
instrumented correctly.
Thanks to Peter Dohm for the bug report.
* Improve the test suite.
[ Alex Gaynor ]
* Fix the afl.loop() docstring.
-- Jakub Wilk Sat, 13 Feb 2016 23:41:05 +0100
python-afl (0.5.1) unstable; urgency=low
* Fix TypeError when built with Cython 0.23.2.
-- Jakub Wilk Fri, 18 Sep 2015 11:12:12 +0200
python-afl (0.5) unstable; urgency=low
* Fix deprecated call to afl.start() in README.
* Make afl.start() emit DeprecationWarning.
* Enable persistent mode only if PYTHON_AFL_PERSISTENT is set.
Let py-afl-fuzz set this variable.
* Don't install the exception hook, unless enabled with PYTHON_AFL_SIGNAL.
Let py-afl-fuzz set this variable to SIGUSR1.
-- Jakub Wilk Wed, 02 Sep 2015 11:12:42 +0200
python-afl (0.4) unstable; urgency=low
* Rename afl.start() as afl.init(), for consistency with AFL >= 1.89b.
The old name is still available, but it's deprecated.
* Add new interface for persistent mode, afl.loop().
Remove the old interface, afl.persistent().
* Improve the test suite.
-- Jakub Wilk Tue, 01 Sep 2015 16:28:06 +0200
python-afl (0.3) unstable; urgency=low
* Implement persistent mode.
* Add docstrings for the exported functions.
* Add __version__.
* Don't rely on the Python hash() function for computing code location
identifiers.
+ Don't set PYTHONHASHSEED in py-afl-fuzz.
+ Remove the py-afl-showmap command.
afl-showmap proper can be now used for Python code.
+ Remove the AflError class. It was only used for checking PYTHONHASHSEED.
* Improve the setup script:
+ Check Cython version.
* Implement a small test suite.
-- Jakub Wilk Mon, 31 Aug 2015 16:56:12 +0200
python-afl (0.2.1) unstable; urgency=low
* Make the setup script install the py-afl-fuzz and py-afl-showmap binaries.
-- Jakub Wilk Tue, 14 Jul 2015 19:34:35 +0200
python-afl (0.2) unstable; urgency=low
* Automatically disable instrumentation when the -n option is provided.
Setting the PYTHON_AFL_DUMB environment variable is no longer needed.
Thanks to Michal Zalewski for the hint how to implement this feature.
* Update the module description to stress that it's dedicated for
pure-Python code.
-- Jakub Wilk Mon, 27 Apr 2015 19:31:08 +0200
python-afl (0.1) unstable; urgency=low
* Initial release.
-- Jakub Wilk Fri, 17 Apr 2015 00:15:16 +0200
python-afl-0.6.1/doc/trophy-case 0000644 0000000 0000000 00000004415 13066710260 016530 0 ustar 00root root 0000000 0000000 The bug-o-rama trophy case
==========================
The following bugs were found with help of *python-afl*:
i18nspector__
-------------
Multiple bugs in the plural expression parser:
| https://github.com/jwilk/i18nspector/commit/c340dc28a1fe
| https://github.com/jwilk/i18nspector/commit/74ac2b9e9882
| https://github.com/jwilk/i18nspector/commit/c9e4fd0efc13
| https://github.com/jwilk/i18nspector/commit/1febfc2bd612
| https://github.com/jwilk/i18nspector/commit/1d671f43497e
| https://github.com/jwilk/i18nspector/commit/0767e9924ab1
| https://github.com/jwilk/i18nspector/commit/1f6993b34ca5
| https://github.com/jwilk/i18nspector/commit/6a76c4884d0b
.. __: http://jwilk.net/software/i18nspector
PyPDF2__
--------
| https://github.com/mstamy2/PyPDF2/issues/184
.. __: https://mstamy2.github.io/PyPDF2/
enzyme__
--------
| https://github.com/Diaoul/enzyme/issues/9
| https://github.com/Diaoul/enzyme/issues/10
| https://github.com/Diaoul/enzyme/issues/11
| https://github.com/Diaoul/enzyme/issues/12
| https://github.com/Diaoul/enzyme/issues/13
| https://github.com/Diaoul/enzyme/issues/14
| https://github.com/Diaoul/enzyme/issues/15
| https://github.com/Diaoul/enzyme/issues/16
| https://github.com/Diaoul/enzyme/issues/17
| https://github.com/Diaoul/enzyme/issues/18
| https://github.com/Diaoul/enzyme/issues/19
| https://github.com/Diaoul/enzyme/issues/20
| https://github.com/Diaoul/enzyme/issues/21
| https://github.com/Diaoul/enzyme/issues/22
.. __: https://github.com/Diaoul/enzyme
PyASN.1__
---------
| https://github.com/pyca/cryptography/issues/1838
.. __: http://pyasn1.sourceforge.net/
gunicorn__
----------
| https://github.com/benoitc/gunicorn/issues/1023
.. __: http://gunicorn.org/
dateutil__
----------
| https://github.com/dateutil/dateutil/issues/82
.. __: https://pypi.python.org/pypi/python-dateutil
regex__
-------
| https://bitbucket.org/mrabarnett/mrab-regex/issues/197/valueerror-in-regexcompile
| https://bitbucket.org/mrabarnett/mrab-regex/issues/198/valueerror-in-regexcompile
| https://bitbucket.org/mrabarnett/mrab-regex/issues/199/segfault-in-recompile
| https://bitbucket.org/mrabarnett/mrab-regex/issues/200/attributeerror-in-regexcompile-with-latest
.. __: https://pypi.python.org/pypi/regex
.. vim:ft=rst ts=3 sts=3 sw=3 et
python-afl-0.6.1/afl.pyx 0000644 0000000 0000000 00000014162 13072234561 015110 0 ustar 00root root 0000000 0000000 # Copyright © 2014-2016 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#cython: autotestdict=False
#cython: c_string_encoding=default
'''
American fuzzy lop fork server and instrumentation for pure-Python code
'''
__version__ = '0.6.1'
cdef object os, signal, struct, sys, warnings
import os
import signal
import struct
import sys
import warnings
# These constants must be kept in sync with afl-fuzz:
DEF SHM_ENV_VAR = b'__AFL_SHM_ID'
DEF FORKSRV_FD = 198
DEF MAP_SIZE_POW2 = 16
DEF MAP_SIZE = 1 << MAP_SIZE_POW2
from cpython.exc cimport PyErr_SetFromErrno
from libc cimport errno
from libc.stddef cimport size_t
from libc.stdint cimport uint32_t
from libc.stdlib cimport getenv
from libc.string cimport strlen
cdef extern from 'sys/shm.h':
unsigned char *shmat(int shmid, void *shmaddr, int shmflg)
cdef unsigned char *afl_area = NULL
cdef unsigned int prev_location = 0
cdef inline unsigned int lhash(const char *key, size_t offset):
# 32-bit Fowler–Noll–Vo hash function
cdef size_t len = strlen(key)
cdef uint32_t h = 0x811C9DC5
while len > 0:
h ^= key[0];
h *= 0x01000193
len -= 1
key += 1
while offset > 0:
h ^= offset;
h *= 0x01000193
offset >>= 8
return h
def _hash(key, offset):
# This function is not a part of public API.
# It is provided only to facilitate testing.
return lhash(key, offset)
cdef object trace
def trace(frame, event, arg):
global prev_location
cdef unsigned int location, offset
location = (
lhash(frame.f_code.co_filename, frame.f_lineno)
% MAP_SIZE
)
offset = location ^ prev_location
prev_location = location // 2
afl_area[offset] += 1
# TODO: make it configurable which modules are instrumented, and which are not
return trace
cdef int except_signal_id = 0
cdef object except_signal_name = os.getenv('PYTHON_AFL_SIGNAL') or '0'
if except_signal_name.isdigit():
except_signal_id = int(except_signal_name)
else:
if except_signal_name[:3] != 'SIG':
except_signal_name = 'SIG' + except_signal_name
except_signal_id = getattr(signal, except_signal_name)
cdef object excepthook
def excepthook(tp, value, traceback):
os.kill(os.getpid(), except_signal_id)
cdef bint init_done = False
cdef int _init(bint persistent_mode) except -1:
global afl_area, init_done
use_forkserver = True
try:
os.write(FORKSRV_FD + 1, b'\0\0\0\0')
except OSError as exc:
if exc.errno == errno.EBADF:
use_forkserver = False
else:
raise
if init_done:
raise RuntimeError('AFL already initialized')
init_done = True
child_stopped = False
child_pid = 0
while use_forkserver:
[child_killed] = struct.unpack('I', os.read(FORKSRV_FD, 4))
if child_stopped and child_killed:
os.waitpid(child_pid, 0)
child_stopped = False
if child_stopped:
os.kill(child_pid, signal.SIGCONT)
child_stopped = False
else:
child_pid = os.fork()
if not child_pid:
# child:
break
# parent:
os.write(FORKSRV_FD + 1, struct.pack('I', child_pid))
(child_pid, status) = os.waitpid(child_pid, os.WUNTRACED if persistent_mode else 0)
child_stopped = os.WIFSTOPPED(status)
os.write(FORKSRV_FD + 1, struct.pack('I', status))
if use_forkserver:
os.close(FORKSRV_FD)
os.close(FORKSRV_FD + 1)
if except_signal_id != 0:
sys.excepthook = excepthook
cdef const char * afl_shm_id = getenv(SHM_ENV_VAR)
if afl_shm_id == NULL:
return 0
afl_area = shmat(int(afl_shm_id), NULL, 0)
if afl_area == -1:
PyErr_SetFromErrno(OSError)
sys.settrace(trace)
return 0
def init():
'''
init()
Start the fork server and enable instrumentation.
This function should be called as late as possible, but before the input is
read, and before any threads are started.
'''
_init(persistent_mode=False)
def start():
'''
deprecated alias for afl.init()
'''
warnings.warn('afl.start() is deprecated, use afl.init() instead', DeprecationWarning)
_init(persistent_mode=False)
cdef bint persistent_allowed = False
cdef unsigned long persistent_counter = 0
def loop(max=None):
'''
while loop([max]):
...
Start the fork server and enable instrumentation,
then run the code inside the loop body in persistent mode.
afl-fuzz >= 1.82b is required for this feature.
'''
global persistent_allowed, persistent_counter
if persistent_counter == 0:
persistent_allowed = os.getenv('PYTHON_AFL_PERSISTENT') is not None
_init(persistent_mode=persistent_allowed)
persistent_counter = 1
return True
cont = persistent_allowed and (
max is None or
persistent_counter < max
)
if cont:
os.kill(os.getpid(), signal.SIGSTOP)
persistent_counter += 1
return True
else:
return False
__all__ = [
'init',
'loop',
]
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/ 0000755 0000000 0000000 00000000000 13136647265 014754 5 ustar 00root root 0000000 0000000 python-afl-0.6.1/tests/target_persistent.py 0000644 0000000 0000000 00000000542 13070775323 021067 0 ustar 00root root 0000000 0000000 import sys
import afl
def main():
s = sys.stdin.read()
if len(s) < 1:
print('Hum?')
sys.exit(1)
s.encode('ASCII')
if s[0] == '0':
print('Looks like a zero to me!')
else:
print('A non-zero value? How quaint!')
if __name__ == '__main__':
while afl.loop():
main()
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/tools.py 0000644 0000000 0000000 00000016130 13136645461 016463 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2013-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import contextlib
import functools
import os
import re
import shutil
import subprocess as ipc
import sys
import tempfile
import traceback
import warnings
import nose.tools
from nose import SkipTest
from nose.tools import (
assert_equal,
assert_not_equal,
assert_true,
)
def noseimport(vmaj, vmin, name=None):
def wrapper(f):
if f.__module__ == 'unittest.case':
return f
if sys.version_info >= (vmaj, vmin):
return getattr(nose.tools, name or f.__name__)
return f
return wrapper
@noseimport(2, 7)
class assert_raises(object):
def __init__(self, exc_type):
self._exc_type = exc_type
self.exception = None
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
if exc_type is None:
assert_true(False, '{0} not raised'.format(self._exc_type.__name__))
if not issubclass(exc_type, self._exc_type):
return False
if isinstance(exc_value, exc_type):
pass
# This branch is not always taken in Python 2.6:
# https://bugs.python.org/issue7853
elif isinstance(exc_value, tuple):
exc_value = exc_type(*exc_value)
else:
exc_value = exc_type(exc_value)
self.exception = exc_value
return True
@noseimport(2, 7, 'assert_raises_regexp')
@noseimport(3, 2)
@contextlib.contextmanager
def assert_raises_regex(exc_type, regex):
with assert_raises(exc_type) as ecm:
yield
assert_regex(str(ecm.exception), regex)
@noseimport(2, 7, 'assert_regexp_matches')
@noseimport(3, 2)
def assert_regex(text, regex):
try:
str_types = basestring
except NameError:
str_types = (str, bytes)
if isinstance(regex, str_types):
regex = re.compile(regex)
if not regex.search(text):
message = "Regex didn't match: {0!r} not found in {1!r}".format(regex.pattern, text)
assert_true(False, msg=message)
@noseimport(3, 2)
@contextlib.contextmanager
def assert_warns_regex(exc_type, regex):
with warnings.catch_warnings(record=True) as wlog:
warnings.simplefilter('always', exc_type)
yield
firstw = None
for warning in wlog:
w = warning.message
if not isinstance(w, exc_type):
continue
if firstw is None:
firstw = w
if re.search(regex, str(w)):
return
if firstw is None:
assert_true(False, msg='{exc} not triggered'.format(exc=exc_type.__name__))
else:
assert_true(False, msg='{exc!r} does not match {re!r}'.format(exc=str(firstw), re=regex))
class IsolatedError(Exception):
pass
def _n_relevant_tb_levels(tb):
n = 0
while tb and '__unittest' not in tb.tb_frame.f_globals:
n += 1
tb = tb.tb_next
return n
def clean_environ():
for key in list(os.environ):
if key.startswith('PYTHON_AFL_'):
del os.environ[key]
os.environ['AFL_SKIP_CPUFREQ'] = '1'
os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1'
os.environ['AFL_NO_AFFINITY'] = '1'
os.environ['AFL_ALLOW_TMP'] = '1' # AFL >= 2.48b
os.environ['PWD'] = '//' + os.getcwd() # poor man's AFL_ALLOW_TMP for AFL << 2.48b
def run(cmd, stdin='', xstatus=0):
child = ipc.Popen(
list(cmd),
stdin=ipc.PIPE,
stdout=ipc.PIPE,
stderr=ipc.PIPE,
preexec_fn=clean_environ,
)
(stdout, stderr) = child.communicate(stdin)
if child.returncode != xstatus:
if str is not bytes:
stderr = stderr.decode('ASCII', 'replace')
print(stderr)
raise ipc.CalledProcessError(child.returncode, cmd[0])
return (stdout, stderr)
def fork_isolation(f):
EXIT_EXCEPTION = 101
EXIT_SKIP_TEST = 102
exit = os._exit # pylint: disable=redefined-builtin,protected-access
# sys.exit() can't be used here, because nose catches all exceptions,
# including SystemExit
@functools.wraps(f)
def wrapper(*args, **kwargs):
readfd, writefd = os.pipe()
pid = os.fork()
if pid == 0:
# child:
os.close(readfd)
try:
f(*args, **kwargs)
except SkipTest as exc:
s = str(exc)
if not isinstance(s, bytes):
s = s.encode('UTF-8')
with os.fdopen(writefd, 'wb') as fp:
fp.write(s)
exit(EXIT_SKIP_TEST)
except Exception: # pylint: disable=broad-except
exctp, exc, tb = sys.exc_info()
s = traceback.format_exception(exctp, exc, tb, _n_relevant_tb_levels(tb))
s = ''.join(s)
if not isinstance(s, bytes):
s = s.encode('UTF-8')
del tb
with os.fdopen(writefd, 'wb') as fp:
fp.write(s)
exit(EXIT_EXCEPTION)
exit(0)
else:
# parent:
os.close(writefd)
with os.fdopen(readfd, 'rb') as fp:
msg = fp.read()
msg = msg
if not isinstance(msg, str):
msg = msg.decode('UTF-8')
msg = msg.rstrip('\n')
pid, status = os.waitpid(pid, 0)
if status == (EXIT_EXCEPTION << 8):
raise IsolatedError('\n\n' + msg)
elif status == (EXIT_SKIP_TEST << 8):
raise SkipTest(msg)
elif status == 0 and msg == '':
pass
else:
raise RuntimeError('unexpected isolated process status {0}'.format(status))
return wrapper
@contextlib.contextmanager
def tempdir():
d = tempfile.mkdtemp(prefix='python-afl.')
try:
yield d
finally:
shutil.rmtree(d)
__all__ = [
'SkipTest',
'assert_equal',
'assert_not_equal',
'assert_raises',
'assert_raises_regex',
'assert_regex',
'assert_true',
'assert_warns_regex',
'fork_isolation',
'run',
'tempdir',
]
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_showmap.py 0000644 0000000 0000000 00000004111 13070536456 020034 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
from .tools import (
assert_equal,
assert_not_equal,
run,
tempdir,
)
here = os.path.dirname(__file__)
target = here + '/target.py'
def run_afl_showmap(stdin, xstdout=None, xstatus=0):
with tempdir() as workdir:
outpath = workdir + '/out'
(stdout, stderr) = run(
['py-afl-showmap', '-o', outpath, sys.executable, target],
stdin=stdin,
xstatus=xstatus,
)
del stderr # make pylint happy
if xstdout is not None:
assert_equal(stdout, xstdout)
with open(outpath, 'r') as file:
return file.read()
def test_diff():
out1 = run_afl_showmap(b'0', xstdout=b'Looks like a zero to me!\n')
out2 = run_afl_showmap(b'1', xstdout=b'A non-zero value? How quaint!\n')
assert_not_equal(out1, out2)
def test_exception():
out = run_afl_showmap(b'\xff',
xstatus=2,
)
assert_not_equal(out, b'')
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_version.py 0000644 0000000 0000000 00000003154 13071005022 020026 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import afl
from .tools import (
assert_equal
)
here = os.path.dirname(__file__)
docdir = here + '/../doc'
def uopen(path):
if str is not bytes:
return open(path, 'rt', encoding='UTF-8')
else:
return open(path, 'rt')
def test_changelog():
path = docdir + '/changelog'
with uopen(path) as file:
line = file.readline()
changelog_version = line.split()[1].strip('()')
assert_equal(changelog_version, afl.__version__)
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/target.py 0000644 0000000 0000000 00000000527 13070775323 016612 0 ustar 00root root 0000000 0000000 import sys
import afl
def main():
s = sys.stdin.read()
if len(s) < 1:
print('Hum?')
sys.exit(1)
s.encode('ASCII')
if s[0] == '0':
print('Looks like a zero to me!')
else:
print('A non-zero value? How quaint!')
if __name__ == '__main__':
afl.init()
main()
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_fuzz.py 0000644 0000000 0000000 00000013320 13070533202 017340 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import print_function
import base64
import contextlib
import distutils.version
import glob
import os
import re
import signal
import subprocess as ipc
import sys
import time
import warnings
try:
# Python >= 3.3
from shlex import quote as shell_quote
except ImportError:
# Python << 3.3
from pipes import quote as shell_quote
from .tools import (
SkipTest,
assert_true,
clean_environ,
tempdir,
)
here = os.path.dirname(__file__)
token = base64.b64encode(os.urandom(8))
if not isinstance(token, str):
token = token.decode('ASCII')
def get_afl_version():
child = ipc.Popen(['afl-fuzz'], stdout=ipc.PIPE)
version = child.stdout.readline()
child.stdout.close()
child.wait()
if str is not bytes:
version = version.decode('ASCII')
version = re.sub(r'\x1b\[[^m]+m', '', version)
match = re.match(r'^afl-fuzz\s+([0-9.]+)b?\b', version)
version = match.group(1)
return distutils.version.StrictVersion(version)
def sleep(n):
time.sleep(n)
return n
def check_core_pattern():
with open('/proc/sys/kernel/core_pattern', 'rb') as file:
pattern = file.read()
if str is not bytes:
pattern = pattern.decode('ASCII', 'replace')
pattern = pattern.rstrip('\n')
if pattern.startswith('|'):
raise SkipTest('/proc/sys/kernel/core_pattern = ' + pattern)
def _test_fuzz(workdir, target, dumb=False):
input_dir = workdir + '/in'
output_dir = workdir + '/out'
os.mkdir(input_dir)
os.mkdir(output_dir)
with open(input_dir + '/in', 'w') as file:
file.write('0')
crash_dir = output_dir + '/crashes'
queue_dir = output_dir + '/queue'
have_crash = False
have_paths = False
n_paths = 0
with open('/dev/null', 'wb') as devnull:
with open(workdir + '/stdout', 'wb') as stdout:
cmdline = ['py-afl-fuzz', '-i', input_dir, '-o', output_dir, '--', sys.executable, target, token]
if dumb:
cmdline[1:1] = ['-n']
print('$ ' + ' '.join(shell_quote(arg) for arg in cmdline))
afl = ipc.Popen(
cmdline,
stdout=stdout,
stdin=devnull,
preexec_fn=clean_environ,
)
try:
timeout = 10
while timeout > 0:
if afl.poll() is not None:
break
have_crash = len(glob.glob(crash_dir + '/id:*')) >= 1
n_paths = len(glob.glob(queue_dir + '/id:*'))
have_paths = (n_paths == 1) if dumb else (n_paths >= 2)
if have_crash and have_paths:
break
timeout -= sleep(0.1)
if afl.returncode is None:
afl.terminate()
afl.wait()
except:
afl.kill()
raise
with open(workdir + '/stdout', 'rb') as file:
stdout = file.read()
if str is not bytes:
stdout = stdout.decode('ASCII', 'replace')
print(stdout)
if not have_crash and '/proc/sys/kernel/core_pattern' in stdout:
check_core_pattern()
assert_true(have_crash, "target program didn't crash")
assert_true(have_paths, 'target program produced {n} distinct paths'.format(n=n_paths))
@contextlib.contextmanager
def stray_process_cleanup():
# afl-fuzz doesn't always kill the target process:
# https://groups.google.com/d/topic/afl-users/E37s4YDti7o
try:
yield
finally:
ps = ipc.Popen(['ps', 'ax'], stdout=ipc.PIPE)
strays = []
for line in ps.stdout:
if not isinstance(line, str):
line = line.decode('ASCII', 'replace')
if token in line:
strays += [line]
if strays:
warnings.warn('stray process{es} left behind:\n{ps}'.format(
es=('' if len(strays) == 1 else 'es'),
ps=''.join(strays)
), category=RuntimeWarning)
for line in strays:
pid = int(line.split()[0])
os.kill(pid, signal.SIGKILL)
ps.wait()
def test_fuzz(dumb=False):
def t(target):
with stray_process_cleanup():
with tempdir() as workdir:
_test_fuzz(
workdir=workdir,
target=os.path.join(here, target),
dumb=dumb,
)
yield t, 'target.py'
yield t, 'target_persistent.py'
def test_fuzz_dumb():
if get_afl_version() < '1.95':
def skip():
raise SkipTest('afl-fuzz >= 1.95b is required')
else:
skip = False
for t in test_fuzz(dumb=True):
yield skip or t
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_loop.py 0000644 0000000 0000000 00000004556 13071005022 017321 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import signal
import afl
from .tools import (
assert_equal,
assert_raises_regex,
fork_isolation,
)
def test_persistent():
_test_persistent(None)
_test_persistent(1, 1)
_test_persistent(1, max=1)
_test_persistent(42, 42)
_test_persistent(42, max=42)
@fork_isolation
def _test_persistent(n, *args, **kwargs):
os.environ['PYTHON_AFL_PERSISTENT'] = '1'
n_max = 1000
k = [0]
def kill(pid, sig):
assert_equal(pid, os.getpid())
assert_equal(sig, signal.SIGSTOP)
k[0] += 1
os.kill = kill
x = 0
while afl.loop(*args, **kwargs):
x += 1
if x == n_max:
break
if n is None:
n = n_max
assert_equal(x, n)
assert_equal(k[0], n - 1)
def test_docile():
_test_docile()
_test_docile(1)
_test_docile(max=1)
_test_docile(42)
_test_docile(max=42)
@fork_isolation
def _test_docile(*args, **kwargs):
os.environ.pop('PYTHON_AFL_PERSISTENT', None)
x = 0
while afl.loop(*args, **kwargs):
x += 1
assert_equal(x, 1)
@fork_isolation
def test_double_init():
afl.init()
with assert_raises_regex(RuntimeError, '^AFL already initialized$'):
while afl.loop():
pass
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_import.py 0000644 0000000 0000000 00000003210 13071152100 017644 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import afl
from .tools import (
assert_equal,
)
exports = [
'init',
'loop',
]
deprecated = [
'start',
]
def wildcard_import(mod):
ns = {}
exec('from {mod} import *'.format(mod=mod), {}, ns)
return ns
def test_wildcard_import():
ns = wildcard_import('afl')
assert_equal(
sorted(ns.keys()),
sorted(exports)
)
def test_dir():
assert_equal(
sorted(o for o in dir(afl) if not o.startswith('_')),
sorted(exports + deprecated)
)
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_tmin.py 0000644 0000000 0000000 00000003574 13070750522 017331 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
from .tools import (
assert_equal,
run,
tempdir,
)
here = os.path.dirname(__file__)
target = here + '/target.py'
def run_afl_tmin(input, xoutput, xstatus=0):
with tempdir() as workdir:
inpath = workdir + '/in'
with open(inpath, 'wb') as file:
file.write(input)
outpath = workdir + '/out'
run(
['py-afl-tmin', '-i', inpath, '-o', outpath, '--', sys.executable, target],
xstatus=xstatus,
)
with open(outpath, 'rb') as file:
output = file.read()
assert_equal(output, xoutput)
def test():
run_afl_tmin(b'0' * 6, b'0')
run_afl_tmin(b'X' * 7, b'X')
def test_exc():
run_afl_tmin(b'\xcf\x87', b'\x87')
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_cmin.py 0000644 0000000 0000000 00000005023 13070751144 017300 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
from .tools import (
assert_equal,
run,
tempdir,
)
here = os.path.dirname(__file__)
target = here + '/target.py'
def run_afl_cmin(input, xoutput, crashes_only=False):
input = sorted(input)
xoutput = sorted(xoutput)
with tempdir() as workdir:
indir = '//{dir}/in'.format(dir=workdir)
outdir = '//{dir}/out'.format(dir=workdir)
for dir in [indir, outdir]:
os.mkdir(dir)
for n, blob in enumerate(input):
path = '{dir}/{n}'.format(dir=indir, n=n)
with open(path, 'wb') as file:
file.write(blob)
cmdline = ['py-afl-cmin', '-i', indir, '-o', outdir, '--', sys.executable, target]
if crashes_only:
cmdline[1:1] = ['-C']
print(cmdline)
run(cmdline)
output = []
for n in os.listdir(outdir):
path = '{dir}/{n}'.format(dir=outdir, n=n)
with open(path, 'rb') as file:
blob = file.read()
output += [blob]
output.sort()
assert_equal(xoutput, output)
def test():
run_afl_cmin([
b'0' * 6, b'0',
b'X' * 7, b'1',
b'\xcf\x87',
], [
b'0',
b'1',
])
def test_crashes_only():
run_afl_cmin([
b'0' * 6, b'0',
b'X' * 7, b'1',
b'\xcf\x87',
], [
b'\xcf\x87',
], crashes_only=True)
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/__init__.py 0000644 0000000 0000000 00000000000 13065453571 017047 0 ustar 00root root 0000000 0000000 python-afl-0.6.1/tests/test_hash.py 0000644 0000000 0000000 00000003002 13071003537 017265 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import afl
from .tools import (
assert_equal,
)
def test_hash():
h = afl._hash # pylint: disable=protected-access
assert_equal(h('', 0), 2166136261)
assert_equal(h('', 42), 789356349)
assert_equal(h('moo', 23), 3934561083)
assert_equal(h('moo', 37), 3162790609)
assert_equal(h('wół', 23), 2298935884)
assert_equal(h('wół', 37), 3137816834)
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/tests/test_init.py 0000644 0000000 0000000 00000003170 13071005022 017302 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import re
import afl
from .tools import (
assert_raises_regex,
assert_warns_regex,
fork_isolation,
)
@fork_isolation
def test_deprecated_start():
msg = 'afl.start() is deprecated, use afl.init() instead'
msg_re = '^{0}$'.format(re.escape(msg))
with assert_warns_regex(DeprecationWarning, msg_re):
afl.start()
@fork_isolation
def test_double_init():
afl.init()
with assert_raises_regex(RuntimeError, '^AFL already initialized$'):
afl.init()
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/setup.py 0000644 0000000 0000000 00000010024 13075714331 015310 0 ustar 00root root 0000000 0000000 # encoding=UTF-8
# Copyright © 2014-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
'''
*python-afl* is an experimental module that enables
`American fuzzy lop`_ fork server and instrumentation for pure-Python code.
.. _American fuzzy lop: http://lcamtuf.coredump.cx/afl/
'''
import glob
import io
import os
import sys
import distutils.core
import distutils.version
from distutils.command.sdist import sdist as distutils_sdist
try:
import distutils644
except ImportError:
pass
else:
distutils644.install()
b = b'' # Python >= 2.6 is required
def get_version():
with io.open('doc/changelog', encoding='UTF-8') as file:
line = file.readline()
return line.split()[1].strip('()')
classifiers = '''
Development Status :: 3 - Alpha
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: POSIX
Programming Language :: Cython
Programming Language :: Python :: 2
Programming Language :: Python :: 3
Topic :: Software Development :: Quality Assurance
Topic :: Software Development :: Testing
'''.strip().splitlines()
meta = dict(
name='python-afl',
version=get_version(),
license='MIT',
description='American fuzzy lop fork server and instrumentation for pure-Python code',
long_description=__doc__.strip(),
classifiers=classifiers,
url='http://jwilk.net/software/python-afl',
author='Jakub Wilk',
author_email='jwilk@jwilk.net',
)
if 'setuptools' in sys.modules and sys.argv[1] == 'egg_info':
# We wouldn't normally want setuptools; but pip forces it upon us anyway,
# so let's abuse it to instruct pip to install Cython if it's missing.
distutils.core.setup(
install_requires=['Cython>=0.19'],
# Conceptually, “setup_requires” would make more sense than
# “install_requires”, but the former is not supported by pip:
# https://github.com/pypa/pip/issues/1820
**meta
)
sys.exit(0)
try:
import Cython
except ImportError:
raise RuntimeError('Cython >= 0.19 is required')
try:
cython_version = Cython.__version__
except AttributeError:
# Cython prior to 0.14 didn't have __version__.
# Oh well. We don't support such old versions anyway.
cython_version = '0'
cython_version = distutils.version.LooseVersion(cython_version)
if cython_version < '0.19':
raise RuntimeError('Cython >= 0.19 is required')
import Cython.Build # pylint: disable=wrong-import-position
class cmd_sdist(distutils_sdist):
def maybe_move_file(self, base_dir, src, dst):
src = os.path.join(base_dir, src)
dst = os.path.join(base_dir, dst)
if os.path.exists(src):
self.move_file(src, dst)
def make_release_tree(self, base_dir, files):
distutils_sdist.make_release_tree(self, base_dir, files)
self.maybe_move_file(base_dir, 'LICENSE', 'doc/LICENSE')
distutils.core.setup(
ext_modules=Cython.Build.cythonize('afl.pyx'),
scripts=glob.glob('py-afl-*'),
cmdclass=dict(sdist=cmd_sdist),
**meta
)
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/py-afl-showmap 0000755 0000000 0000000 00000000422 13065510071 016363 0 ustar 00root root 0000000 0000000 #!/bin/sh
export PYTHON_AFL_SIGNAL=${PYTHON_AFL_SIGNAL:-SIGUSR1}
if ! command -v afl-showmap > /dev/null
then
cat >&2 < installed?
EOF
exit 127
fi
exec afl-showmap "$@"
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/py-afl-cmin 0000755 0000000 0000000 00000000445 13070751117 015644 0 ustar 00root root 0000000 0000000 #!/bin/sh
export AFL_SKIP_BIN_CHECK=1
export PYTHON_AFL_SIGNAL=${PYTHON_AFL_SIGNAL:-SIGUSR1}
if ! command -v afl-cmin > /dev/null
then
cat >&2 < installed?
EOF
exit 127
fi
exec afl-cmin "$@"
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/py-afl-fuzz 0000755 0000000 0000000 00000001106 13065510073 015705 0 ustar 00root root 0000000 0000000 #!/bin/sh
export AFL_SKIP_CHECKS=1 # AFL << 1.20b
export AFL_SKIP_BIN_CHECK=1 # AFL >= 1.20b
export AFL_DUMB_FORKSRV=1
if [ -n "$PYTHON_AFL_DUMB" ]
then
# shellcheck disable=SC2016
printf '%s: $PYTHON_AFL_DUMB is deprecated; use -n instead\n' "$(basename "$0")" >&2
set -- -n "$@"
fi
export PYTHON_AFL_SIGNAL=${PYTHON_AFL_SIGNAL:-SIGUSR1}
export PYTHON_AFL_PERSISTENT=1
if ! command -v afl-fuzz > /dev/null
then
cat >&2 < installed?
EOF
exit 127
fi
exec afl-fuzz "$@"
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/PKG-INFO 0000644 0000000 0000000 00000001625 13136647265 014713 0 ustar 00root root 0000000 0000000 Metadata-Version: 1.1
Name: python-afl
Version: 0.6.1
Summary: American fuzzy lop fork server and instrumentation for pure-Python code
Home-page: http://jwilk.net/software/python-afl
Author: Jakub Wilk
Author-email: jwilk@jwilk.net
License: MIT
Description: *python-afl* is an experimental module that enables
`American fuzzy lop`_ fork server and instrumentation for pure-Python code.
.. _American fuzzy lop: http://lcamtuf.coredump.cx/afl/
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
python-afl-0.6.1/private/ 0000755 0000000 0000000 00000000000 13136647265 015264 5 ustar 00root root 0000000 0000000 python-afl-0.6.1/private/update-version 0000755 0000000 0000000 00000000266 13121250156 020142 0 ustar 00root root 0000000 0000000 #!/bin/sh
version=${1:?"no version number provided"}
set -e
set -x
dch -m -v "$version" -u low -c doc/changelog
sed -i -E -e "s/^(__version__) = '[0-9.]+'$/\1 = '$version'/" afl.pyx
python-afl-0.6.1/private/check-rst 0000755 0000000 0000000 00000002740 13070537622 017070 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Copyright © 2016-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
here=$(dirname "$0")
rst2xml=$(command -v rst2xml) \
|| rst2xml=$(command -v rst2xml.py) \
|| { printf 'rst2xml not found\n' >&2; exit 1; }
options='--input-encoding=UTF-8 --output-encoding=UTF-8 --strict'
if [ $# -eq 0 ]
then
grep -rwl 'ft[=]rst' "$here/.."
else
printf '%s\n' "$@"
fi |
xargs -L1 -t -I{} "$rst2xml" $options {} /dev/null
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/private/run-pydiatra 0000755 0000000 0000000 00000002370 13073772740 017630 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Copyright © 2016-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
PYTHON=${PYTHON:-python}
if [ $# -eq 0 ]
then
set -- $(find . -name '*.py')
fi
exec "$PYTHON" -m pydiatra "$@"
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/private/build-and-test 0000755 0000000 0000000 00000004037 13071152052 020011 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
usage()
{
printf 'Usage: %s [--no-build] [pythonX.Y...]\n' "$0"
}
args=$(getopt -n "$0" -o 'hj:' --long 'help,jobs:,no-build' -- "$@")
if [ $? -ne 0 ]
then
usage >&2
exit 1
fi
eval set -- "$args"
opt_jobs=$(nproc) || opt_jobs=1
opt_build=y
while true
do
case "$1" in
-h|--help) usage; exit 0;;
-j|--jobs) opt_jobs=$2; shift 2;;
--no-build) opt_build=; shift;;
--) shift; break;;
*) printf '%s: internal error (%s)\n' "$0" "$1" >&2; exit 1;;
esac
done
set -e
[ $# = 0 ] && set -- python
[ -z $opt_build ] ||
printf '%s\n' "$@" \
| xargs -P"$opt_jobs" -t -I'{python}' env '{python}' setup.py build --build-lib 'build/{python}'
cd tests
nosetests=$(command -v nosetests) || { echo nosetests not found >&2; exit 1; }
export PATH="$PWD/..:$PATH"
printf '%s\n' "$@" \
| xargs -t -I'{python}' env PYTHONPATH="$PWD/../build/{python}" '{python}' "$nosetests" --verbose
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/private/run-pylint 0000755 0000000 0000000 00000003704 13071005022 017310 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
set -e -u
PYTHON=${PYTHON:-python}
[ -n "${TRAVIS_PYTHON_VERSION:-}" ] && PYTHON=python
"$PYTHON" -m pylint --version >/dev/null || exit 1
if [ $# -eq 0 ]
then
set -- setup.py tests/*.py
fi
if [ -n "${TRAVIS_PYTHON_VERSION:-}" ]
then
# https://github.com/PyCQA/pylint/issues/73
set -- --ignored-modules=distutils "$@"
fi
set -- --load-plugins=pylint.extensions.check_elif "$@"
log=$(mktemp -t pylint.XXXXXX)
"$PYTHON" -m pylint "$@" > "$log" || [ $? != 1 ]
! grep '^\w:' "$log" \
| grep -v -P ": redefined-builtin \\[.*\\] Redefining built-in '(file|dir|input)'$" \
| grep -v -P ": redundant-unittest-assert \\[.*\\] Redundant use of assertTrue with constant value False$" \
| grep -v -P ": superfluous-parens \\[.*\\] Unnecessary parens after u?'print' keyword$" \
| LC_ALL=C sort -k2 \
| grep '.' || exit 1
rm "$log"
# vim:ts=4 sts=4 sw=4 et
python-afl-0.6.1/private/run-pyflakes 0000755 0000000 0000000 00000002644 13070772475 017637 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Copyright © 2016-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
PYTHON=${PYTHON:-python}
pyflakes=$(command -v pyflakes) || { echo pyflakes not found >&2; exit 1; }
if [ $# -eq 0 ]
then
set -- $(find . -name '*.py')
fi
# It would be tempting to use "python -m pyflakes" here,
# but that doesn't work in Python 2.6.
exec "$PYTHON" "$pyflakes" "$@"
# vim:ts=4 sts=4 sw=4 et