Skip to content

Commit 25cb72c

Browse files
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.
1 parent 0f49232 commit 25cb72c

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid a debug-build assertion in list sorting when its specialized comparison
2+
cross-check hits the C stack overflow guard.

Objects/listobject.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,6 +2810,26 @@ unsafe_object_compare(PyObject *v, PyObject *w, MergeState *ms)
28102810
return res;
28112811
}
28122812

2813+
#ifndef NDEBUG
2814+
static void
2815+
assert_compare_consistent(PyObject *v, PyObject *w, int res)
2816+
{
2817+
int cmp = PyObject_RichCompareBool(v, w, Py_LT);
2818+
if (cmp < 0) {
2819+
/* This debug-only cross-check can hit the C stack guard even when
2820+
* the specialized comparison itself is safe. Keep the optimized path
2821+
* side-effect free by discarding that exception.
2822+
*/
2823+
assert(PyErr_ExceptionMatches(PyExc_RecursionError));
2824+
PyErr_Clear();
2825+
return;
2826+
}
2827+
assert(res == cmp);
2828+
}
2829+
#else
2830+
# define assert_compare_consistent(v, w, res) ((void)0)
2831+
#endif
2832+
28132833
/* Latin string compare: safe for any two latin (one byte per char) strings. */
28142834
static int
28152835
unsafe_latin_compare(PyObject *v, PyObject *w, MergeState *ms)
@@ -2830,7 +2850,7 @@ unsafe_latin_compare(PyObject *v, PyObject *w, MergeState *ms)
28302850
res < 0 :
28312851
PyUnicode_GET_LENGTH(v) < PyUnicode_GET_LENGTH(w));
28322852

2833-
assert(res == PyObject_RichCompareBool(v, w, Py_LT));;
2853+
assert_compare_consistent(v, w, res);
28342854
return res;
28352855
}
28362856

@@ -2855,7 +2875,7 @@ unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms)
28552875
w0 = _PyLong_CompactValue(wl);
28562876

28572877
res = v0 < w0;
2858-
assert(res == PyObject_RichCompareBool(v, w, Py_LT));
2878+
assert_compare_consistent(v, w, res);
28592879
return res;
28602880
}
28612881

@@ -2870,7 +2890,7 @@ unsafe_float_compare(PyObject *v, PyObject *w, MergeState *ms)
28702890
assert(Py_IS_TYPE(w, &PyFloat_Type));
28712891

28722892
res = PyFloat_AS_DOUBLE(v) < PyFloat_AS_DOUBLE(w);
2873-
assert(res == PyObject_RichCompareBool(v, w, Py_LT));
2893+
assert_compare_consistent(v, w, res);
28742894
return res;
28752895
}
28762896

0 commit comments

Comments
 (0)