From 25cb72c70fb073ba80c4c7421154cb26dda3e07d Mon Sep 17 00:00:00 2001 From: Asish Kumar Date: Thu, 9 Apr 2026 18:26:48 +0000 Subject: [PATCH] gh-148268: Avoid debug assertion in specialized list compares Handle recursion errors raised by the debug-only cross-check in the specialized latin, compact-int, and float comparison helpers used by list sorting. When the verification path hits the C stack overflow guard, clear that exception so the optimized comparison remains side-effect free.\n\nThe change preserves the consistency assertion when the fallback comparison succeeds and adds a NEWS entry for the fix. --- ...-04-09-18-30-00.gh-issue-148268.7cF3yM.rst | 2 ++ Objects/listobject.c | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-18-30-00.gh-issue-148268.7cF3yM.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-18-30-00.gh-issue-148268.7cF3yM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-18-30-00.gh-issue-148268.7cF3yM.rst new file mode 100644 index 00000000000000..598794336cbafe --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-18-30-00.gh-issue-148268.7cF3yM.rst @@ -0,0 +1,2 @@ +Avoid a debug-build assertion in list sorting when its specialized comparison +cross-check hits the C stack overflow guard. diff --git a/Objects/listobject.c b/Objects/listobject.c index 5c9fd55bab1b22..eaf0db6084deca 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2810,6 +2810,26 @@ unsafe_object_compare(PyObject *v, PyObject *w, MergeState *ms) return res; } +#ifndef NDEBUG +static void +assert_compare_consistent(PyObject *v, PyObject *w, int res) +{ + int cmp = PyObject_RichCompareBool(v, w, Py_LT); + if (cmp < 0) { + /* This debug-only cross-check can hit the C stack guard even when + * the specialized comparison itself is safe. Keep the optimized path + * side-effect free by discarding that exception. + */ + assert(PyErr_ExceptionMatches(PyExc_RecursionError)); + PyErr_Clear(); + return; + } + assert(res == cmp); +} +#else +# define assert_compare_consistent(v, w, res) ((void)0) +#endif + /* Latin string compare: safe for any two latin (one byte per char) strings. */ static int unsafe_latin_compare(PyObject *v, PyObject *w, MergeState *ms) @@ -2830,7 +2850,7 @@ unsafe_latin_compare(PyObject *v, PyObject *w, MergeState *ms) res < 0 : PyUnicode_GET_LENGTH(v) < PyUnicode_GET_LENGTH(w)); - assert(res == PyObject_RichCompareBool(v, w, Py_LT));; + assert_compare_consistent(v, w, res); return res; } @@ -2855,7 +2875,7 @@ unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms) w0 = _PyLong_CompactValue(wl); res = v0 < w0; - assert(res == PyObject_RichCompareBool(v, w, Py_LT)); + assert_compare_consistent(v, w, res); return res; } @@ -2870,7 +2890,7 @@ unsafe_float_compare(PyObject *v, PyObject *w, MergeState *ms) assert(Py_IS_TYPE(w, &PyFloat_Type)); res = PyFloat_AS_DOUBLE(v) < PyFloat_AS_DOUBLE(w); - assert(res == PyObject_RichCompareBool(v, w, Py_LT)); + assert_compare_consistent(v, w, res); return res; }