classification
Title: Add PYTHONMALLOC env var and add support for malloc debug hooks in release mode
Type: enhancement Stage: patch review
Components: Versions: Python 3.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: haypo, lemburg, python-dev, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2016-03-09 11:15 by haypo, last changed 2016-04-19 15:03 by python-dev. This issue is now closed.

Files
File name Uploaded Description Edit
pymem.patch haypo, 2016-03-09 11:15 review
pymem-2.patch haypo, 2016-03-09 14:14 review
pymem-3.patch haypo, 2016-03-09 14:36 review
pymem-4.patch haypo, 2016-03-10 17:15 review
Messages (19)
msg261413 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 11:15
Attached patch:

- Add PYTHONMALLOC env var which accepts 4 values:

  * pymalloc: use pymalloc for PyObject_Malloc(), malloc for PyMem_Malloc() and PyMem_RawMalloc()
  * pymalloc_debug: pymalloc + debug hooks
  * malloc: use malloc for PyObject_Malloc(), PyMem_Malloc() and PyMem_RawMalloc()
  * malloc_debug: malloc + debug hooks

- Add support for debug hooks in release mode
- Add unit test for debug hooks in test_capi.py
- Add unit on misuse of memory allocators in test_capi.py

PYTHONMALLOC is used even if -E command line option is used, I prefer to keep the code simple. PYTHONMALLOC must be checked before the first call to any Python memory allocator, so it must occur very earlier. Well, it's simply the first instruction of main()...

My main use case is to be able to use debug hooks to detect:

* misuse of python memory allocators like object allocated with PyObject_Malloc() and freed with PyMem_Free()
* buffer overflow
* buffer underlow
msg261414 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 11:45
The output of sys._debugmallocstats() contains different info:

* Stats of type free lists
* pymalloc stats
* number of calls to memory allocators

The available info depends on:

* pymalloc disabled at compilation with ./configure --without-pymalloc
* pymalloc disabled at runtime with PYTHONMALLOC=malloc or PYTHONMALLOC=malloc_debug
* debug hooks adds the "times object malloc called" counter: by default if Python is compiled with ./configure --with-pydebug, or enabled at runtime using PYTHONMALLOC=pymalloc_debug or PYTHONMALLOC=malloc_debug
msg261415 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 11:47
Example with Python compiled in release mode.

By default, a buffer overflow is not detected. It may crash later, in a random place...

$ ./python -c 'import _testcapi; _testcapi.pymem_buffer_overflow()'


Enabling debug hooks detects the buffer overflow immediatly:


$ PYTHONMALLOC=pymalloc_debug ./python -c 'import _testcapi; _testcapi.pymem_buffer_overflow()'
Debug memory block at address p=0x1a7f490: API 'm'
    16 bytes originally requested
    The 7 pad bytes at p-7 are FORBIDDENBYTE, as expected.
    The 8 pad bytes at tail=0x1a7f4a0 are not all FORBIDDENBYTE (0xfb):
        at tail+0: 0x78 *** OUCH
        at tail+1: 0xfb
        at tail+2: 0xfb
        at tail+3: 0xfb
        at tail+4: 0xfb
        at tail+5: 0xfb
        at tail+6: 0xfb
        at tail+7: 0xfb
    The block was made by call #35014 to debug malloc/realloc.
    Data at p: cb cb cb cb cb cb cb cb cb cb cb cb cb cb cb cb
Fatal Python error: bad trailing pad byte

Current thread 0x00007fca30572700 (most recent call first):
  File "<string>", line 1 in <module>
Abandon (core dumped)
msg261416 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 11:50
The motivation to support PYTHONMALLOC=malloc (always use malloc) and not just PYTHONMALLOC=debug (enable debug hooks) is to allow to use external memory debugger like Valgrind.

Valgrind doesn't like pymalloc (pymalloc raises false alarms), we had to a configuration option for it: ./configure --with-valgrind.

Using PYTHONMALLOC=malloc allows to use Valgrind on the system Python, it's easier to use. No need to recompile Python.
msg261425 - (view) Author: Roundup Robot (python-dev) Date: 2016-03-09 14:02
New changeset af2fc10f703b by Victor Stinner in branch '3.5':
Issue #26516: Enhance Python mem allocators doc
https://hg.python.org/cpython/rev/af2fc10f703b
msg261428 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 14:14
More complete patch (version 2):

* Document PYTHONMALLOC environment variable
* Add PYTHONMALLOC=debug to install debug hooks without forcing a specific memory allocator (keep the default memory allocator)
* Fix sys._debugmallocstats() for PYTHONMALLOC=malloc: don't display pymalloc stats
* _testcapi.pymem_api_misuse(): more realistic code, use PyMem_Mallloc() + PyMem_RawFree(). This code works in release mode (since PyMem and PyMem_Raw use the same allocator: malloc/free), but fail with a fatal error with debug hooks (API misused).
msg261429 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 14:25
When I wrote the PEP 445, I already proposed to add a new environment variable to install debug hooks on a Python compiled in release mode:

https://www.python.org/dev/peps/pep-0445/#add-a-new-pydebugmalloc-environment-variable

But the proposition was rejected because of the PEP 432, but 3 years later, this PEP is still stuck at the draft state. So I propose to move on.

Anyway, PYTHONMALLOC is really a corner case since it must be read *very early* (first instruction of main()).


Oh by the way, I forgot to explain the initial motivation for this change! It's the issue #26249: "Change PyMem_Malloc to use pymalloc allocator". With this change, applications misuing PyMem_Malloc API (ex: alloc memory with PyMem_Malloc(), free memory with free()) will start to crash. PYTHONMALLOC=debug should help these applications to detect bugs easier and get an obvious error message.
msg261432 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-09 14:36
Patch 3:

- Ooops, I updated pymem_api_misuse(), but I forgot to update the related unit test. It's now fixed.
msg261514 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-10 17:15
Oh, I forgot to update wmain() for Windows.

Patch 4 handles wmain() too. I added a few comments and reformatted some parts of the code.
msg261746 - (view) Author: Roundup Robot (python-dev) Date: 2016-03-14 12:41
New changeset aa280432e9c7 by Victor Stinner in branch 'default':
Add PYTHONMALLOC env var
https://hg.python.org/cpython/rev/aa280432e9c7
msg261747 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-14 12:45
I reworked my patch before pushing it:

* it now respects -E and -I command line options
* I documented changes in Doc/, Misc/NEWS, What's New in Python 3.6, Misc/README.valgrind, etc.
* Debug hooks are now also installed when Python is compiled in debug mode without pymalloc
* PYTHONMALLOCSTATS is now ignored if the allocator if pymalloc is not used
* PYTHONMALLOC is ignored if it's an empty string
msg261748 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-14 12:56
Note: I just checked PYTHONMALLOCSTATS=1 with -X tracemalloc. It looks like it works as expected. Tracemalloc hooks are unregistered before stats are displayed at exit, _Pymem_PymallocEnabled() returns 1 as expected.
msg261761 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-14 15:11
test_capi fails on Windows:
http://buildbot.python.org/all/builders/AMD64%20Windows7%20SP1%203.x/builds/7348/steps/test/logs/stdio

Probably a issue with newline characters.
msg261765 - (view) Author: Roundup Robot (python-dev) Date: 2016-03-14 16:10
New changeset 3c3df33f2655 by Victor Stinner in branch 'default':
Issue #26516: Fix test_capi on Windows
https://hg.python.org/cpython/rev/3c3df33f2655
msg261769 - (view) Author: Roundup Robot (python-dev) Date: 2016-03-14 16:48
New changeset d8d6ec1333e6 by Victor Stinner in branch 'default':
Issue #26516: Fix test_capi on 32-bit system
https://hg.python.org/cpython/rev/d8d6ec1333e6
msg261777 - (view) Author: Roundup Robot (python-dev) Date: 2016-03-14 20:58
New changeset 7534eb7bd57e by Victor Stinner in branch 'default':
Issue #26516: Fix test_capi on AIX
https://hg.python.org/cpython/rev/7534eb7bd57e
msg261872 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2016-03-17 00:41
Buildbots are happy, I close the issue.
msg261955 - (view) Author: Roundup Robot (python-dev) Date: 2016-03-18 10:04
New changeset 7b079adb0774 by Victor Stinner in branch 'default':
Enhance documentation on malloc debug hooks
https://hg.python.org/cpython/rev/7b079adb0774
msg263746 - (view) Author: Roundup Robot (python-dev) Date: 2016-04-19 15:03
New changeset c4c14e34e528 by Victor Stinner in branch 'default':
Don't define _PyMem_PymallocEnabled() if pymalloc is disabled
https://hg.python.org/cpython/rev/c4c14e34e528
History
Date User Action Args
2016-04-19 15:03:12python-devsetmessages: + msg263746
2016-03-18 10:04:44python-devsetmessages: + msg261955
2016-03-17 00:41:28hayposetstatus: open -> closed
resolution: fixed
messages: + msg261872
2016-03-14 20:58:35python-devsetmessages: + msg261777
2016-03-14 16:48:46python-devsetmessages: + msg261769
2016-03-14 16:10:58python-devsetmessages: + msg261765
2016-03-14 15:11:07hayposetmessages: + msg261761
2016-03-14 12:56:07hayposetmessages: + msg261748
2016-03-14 12:45:58hayposetmessages: + msg261747
2016-03-14 12:41:28python-devsetmessages: + msg261746
2016-03-10 17:15:17hayposetfiles: + pymem-4.patch

messages: + msg261514
2016-03-09 14:36:36hayposetfiles: + pymem-3.patch

messages: + msg261432
2016-03-09 14:25:58hayposetmessages: + msg261429
2016-03-09 14:15:00hayposetfiles: + pymem-2.patch

messages: + msg261428
2016-03-09 14:10:50serhiy.storchakasetstage: patch review
2016-03-09 14:02:42python-devsetnosy: + python-dev
messages: + msg261425
2016-03-09 11:50:47hayposetmessages: + msg261416
2016-03-09 11:47:37hayposetmessages: + msg261415
2016-03-09 11:45:27hayposetmessages: + msg261414
2016-03-09 11:15:15haypocreate