https://github.com/python/cpython/commit/2791c35cae1fa274198bb2d3b3dccb378dfe40f3
commit: 2791c35cae1fa274198bb2d3b3dccb378dfe40f3
branch: main
author: Vasiliy Kiryanov <[email protected]>
committer: encukou <[email protected]>
date: 2026-10-07T18:05:37+02:00
summary:
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.
files:
A Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
M Doc/c-api/memory.rst
M Doc/using/configure.rst
M Include/internal/pycore_pymem_init.h
M Lib/test/test_cmd_line.py
M Lib/test/test_sys.py
M Objects/obmalloc.c
M configure
M configure.ac
diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst
index 73310670ac371c..94be7dde89dbf3 100644
--- a/Doc/c-api/memory.rst
+++ b/Doc/c-api/memory.rst
@@ -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
----------------------------------
diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst
index ab4ec42a575369..8a510a2dc0ba61 100644
--- a/Doc/using/configure.rst
+++ b/Doc/using/configure.rst
@@ -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
diff --git a/Include/internal/pycore_pymem_init.h
b/Include/internal/pycore_pymem_init.h
index 2a0e0817dcc7f8..f89eb8bfc4964c 100644
--- a/Include/internal/pycore_pymem_init.h
+++ b/Include/internal/pycore_pymem_init.h
@@ -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);
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 1e303dc3bf6770..0af0debd8150e3 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -845,10 +845,13 @@ def test_xdev(self):
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 @@ def test_pythonmalloc(self):
# 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'
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 1ccdcf5a187bbb..c74ca05ca66c9c 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1194,8 +1194,10 @@ def test_debugmallocstats(self):
# 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
diff --git
a/Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
b/Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
new file mode 100644
index 00000000000000..7e51844695d0e4
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
@@ -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.
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index 9a41a422467223..841fb796e1ced1 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -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
diff --git a/configure b/configure
index c0c0e5b193c206..d6f5319beb7037 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/configure.ac b/configure.ac
index 9e83af6c2d95ca..9ed32b686f033a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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])])
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]