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; }