gh-136872: Build pymalloc in sanitizer builds, but disable it by default (GH-157934)

--with-*address-sanitizer and --with-memory-sanitizer unconditionally
disabled pymalloc (even when --with-pymalloc was given explicitly).

Enable it, so that it's available with `PYTHONMALLOC=pymalloc`.
Disable it by default on the affected sanitizer builds.
This commit is contained in:
Vasiliy Kiryanov authored and GitHub committed 2026-10-07 18:05:37 +02:00
1 parent acfa77671c
commit 2791c35cae
9 files changed
+48 -28

No files matched your search

+11 -3
View File
@@ -437,6 +437,8 @@ Release build ``"pymalloc"`` ``malloc``
Debug build ``"pymalloc_debug"`` ``malloc`` + debug ``pymalloc`` + debug ``pymalloc`` + debug
Release build, without pymalloc ``"malloc"`` ``malloc`` ``malloc`` ``malloc``
Debug build, without pymalloc ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug
Release build, with ASan or MSan ``"malloc"`` ``malloc`` ``malloc`` ``malloc``
Debug build, with ASan or MSan ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug
Free-threaded build ``"mimalloc"`` ``mimalloc`` ``mimalloc`` ``mimalloc``
Free-threaded debug build ``"mimalloc_debug"`` ``mimalloc`` + debug ``mimalloc`` + debug ``mimalloc`` + debug
=================================== ======================= ==================== ====================== ======================
@@ -451,6 +453,10 @@ Legend:
* "+ debug": with :ref:`debug hooks on the Python memory allocators
<pymem-debug-hooks>`.
* "Debug build": :ref:`Python build in debug mode <debug-build>`.
* "with ASan or MSan": sanitizer build as configured using the
:option:`--with-address-sanitizer`,
:option:`--with-hwaddress-sanitizer`, and/or
:option:`--with-memory-sanitizer` option.
.. _customize-memory-allocators:
@@ -705,9 +711,11 @@ This allocator is disabled if Python is configured with the
:option:`--without-pymalloc` option. It can also be disabled at runtime using
the :envvar:`PYTHONMALLOC` environment variable (ex: ``PYTHONMALLOC=malloc``).
Typically, it makes sense to disable the pymalloc allocator when building
Python with AddressSanitizer (:option:`--with-address-sanitizer`) which helps
uncover low level bugs within the C code.
The pymalloc allocator is disabled by default when Python is built with
a sanitizer which does not track pymalloc allocations
(:option:`--with-address-sanitizer`, :option:`--with-hwaddress-sanitizer`,
:option:`--with-memory-sanitizer`).
Use :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to enable pymalloc.
Customize pymalloc Arena Allocator
----------------------------------
+9 -3
View File
@@ -1016,9 +1016,9 @@ Debug options
.. option:: --with-address-sanitizer
Enable AddressSanitizer memory error detector, ``asan`` (default is no).
To improve ASan detection capabilities you may also want to combine this
with :option:`--without-pymalloc` to disable the specialized small-object
allocator whose allocations are not tracked by ASan.
When built with ``asan``, Python uses ``malloc`` instead of :ref:`pymalloc <pymalloc>` by default.
Set :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
.. versionadded:: 3.6
@@ -1033,6 +1033,9 @@ Debug options
<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_
for more information.
When built with ``hwasan``, Python uses ``malloc`` instead of :ref:`pymalloc <pymalloc>` by default.
Set :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
.. versionadded:: next
.. option:: --with-memory-sanitizer
@@ -1043,6 +1046,9 @@ Debug options
not built with MSan, so either build all dependencies with MSan or disable
the extension modules that use them in :file:`Modules/Setup.local`.
When built with ``msan``, Python uses ``malloc`` instead of :ref:`pymalloc <pymalloc>` by default.
Set :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
.. versionadded:: 3.6
.. option:: --with-undefined-behavior-sanitizer
+4
View File
@@ -36,6 +36,10 @@ extern void _PyMem_MiRawFree(void *, void *);
extern void* _PyMem_MiRawRealloc(void *, void *, size_t);
# undef PYRAW_ALLOC
# define PYRAW_ALLOC {NULL, _PyMem_MiRawMalloc, _PyMem_MiRawCalloc, _PyMem_MiRawRealloc, _PyMem_MiRawFree}
#elif defined(_Py_ADDRESS_SANITIZER) || defined(_Py_MEMORY_SANITIZER)
// Keep in sync with the default allocators in Objects/obmalloc.c.
# define PYOBJ_ALLOC PYRAW_ALLOC
# define PYMEM_ALLOC PYOBJ_ALLOC
#elif defined(WITH_PYMALLOC)
extern void* _PyObject_Malloc(void *, size_t);
extern void* _PyObject_Calloc(void *, size_t, size_t);
+11 -3
View File
@@ -845,10 +845,13 @@ class CmdLineTest(unittest.TestCase):
code = "import _testinternalcapi; print(_testinternalcapi.pymem_getallocatorsname())"
with support.SuppressCrashReport():
out = self.run_xdev("-c", code, check_exitcode=False)
if support.with_pymalloc():
alloc_name = "pymalloc_debug"
elif support.Py_GIL_DISABLED:
if support.Py_GIL_DISABLED:
alloc_name = "mimalloc_debug"
elif support.check_sanitizer(address=True, memory=True):
# ASan and MSan builds default to malloc, even with pymalloc.
alloc_name = "malloc_debug"
elif support.with_pymalloc():
alloc_name = "pymalloc_debug"
else:
alloc_name = "malloc_debug"
self.assertEqual(out, alloc_name)
@@ -927,10 +930,15 @@ class CmdLineTest(unittest.TestCase):
# Test the PYTHONMALLOC environment variable
malloc = not support.Py_GIL_DISABLED
pymalloc = support.with_pymalloc()
sanitizer = support.check_sanitizer(address=True, memory=True)
mimalloc = support.with_mimalloc()
if support.Py_GIL_DISABLED:
default_name = 'mimalloc_debug' if support.Py_DEBUG else 'mimalloc'
default_name_debug = 'mimalloc_debug'
elif sanitizer:
# ASan and MSan builds default to malloc, even with pymalloc.
default_name = 'malloc_debug' if support.Py_DEBUG else 'malloc'
default_name_debug = 'malloc_debug'
elif pymalloc:
default_name = 'pymalloc_debug' if support.Py_DEBUG else 'pymalloc'
default_name_debug = 'pymalloc_debug'
+3 -1
View File
@@ -1194,8 +1194,10 @@ class SysModuleTest(unittest.TestCase):
# The sysconfig vars are not available on Windows.
if sys.platform != "win32":
with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC")
with_sanitizer = support.check_sanitizer(address=True, memory=True)
self.assertIn(b"free PyDictObjects", err)
if with_pymalloc:
# ASan and MSan builds default to malloc, even with pymalloc.
if with_pymalloc and not with_sanitizer:
self.assertIn(b'Small block threshold', err)
# The function has no parameter
@@ -0,0 +1,4 @@
Python built with :option:`--with-address-sanitizer` or
:option:`--with-memory-sanitizer` now includes pymalloc, but uses ``malloc``
as the default memory allocator. Set
:envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
+6
View File
@@ -433,6 +433,12 @@ void* _PyObject_Realloc(void *ctx, void *ptr, size_t size);
# define PYRAW_ALLOC MIMALLOC_RAWALLOC
# define PYMEM_ALLOC MIMALLOC_ALLOC
# define PYOBJ_ALLOC MIMALLOC_OBJALLOC
#elif defined(_Py_ADDRESS_SANITIZER) || defined(_Py_MEMORY_SANITIZER)
// ASan and MSan do not track pymalloc blocks, so use malloc by default.
// pymalloc can still be selected at runtime.
# define PYRAW_ALLOC MALLOC_ALLOC
# define PYMEM_ALLOC MALLOC_ALLOC
# define PYOBJ_ALLOC MALLOC_ALLOC
#elif defined(WITH_PYMALLOC)
# define PYRAW_ALLOC MALLOC_ALLOC
# define PYMEM_ALLOC PYMALLOC_ALLOC
Generated Vendored
-9
View File
@@ -14151,8 +14151,6 @@ then :
printf "%s\n" "$withval" >&6; }
BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=address $LDFLAGS"
# ASan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"
else case e in #(
e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
@@ -14227,11 +14225,6 @@ else case e in #(
esac
fi
# HWASan works by controlling memory allocation, our own malloc interferes,
# so disable it by default, but allow --with-pymalloc to override.
if test -z "$with_pymalloc"; then
with_pymalloc="no"
fi
else case e in #(
e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
@@ -14293,8 +14286,6 @@ else case e in #(
esac
fi
# MSan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"
else case e in #(
e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-9
View File
@@ -3526,8 +3526,6 @@ AC_ARG_WITH([address_sanitizer],
AC_MSG_RESULT([$withval])
BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=address $LDFLAGS"
# ASan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"
],
[AC_MSG_RESULT([no])])
@@ -3551,11 +3549,6 @@ AX_CHECK_COMPILE_FLAG([$hwasan_flags],[
BASECFLAGS="$hwasan_flags -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="$hwasan_flags $LDFLAGS"
],[AC_MSG_ERROR([The selected compiler doesn't support hardware address sanitizer])])
# HWASan works by controlling memory allocation, our own malloc interferes,
# so disable it by default, but allow --with-pymalloc to override.
if test -z "$with_pymalloc"; then
with_pymalloc="no"
fi
],
[AC_MSG_RESULT([no])])
@@ -3572,8 +3565,6 @@ AX_CHECK_COMPILE_FLAG([-fsanitize=memory],[
BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS"
],[AC_MSG_ERROR([The selected compiler doesn't support memory sanitizer])])
# MSan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"
],
[AC_MSG_RESULT([no])])