GH-130396: Use computed stack limits on linux (GH-130398)

* Implement C recursion protection with limit pointers for Linux, MacOS and Windows

* Remove calls to PyOS_CheckStack

* Add stack protection to parser

* Make tests more robust to low stacks

* Improve error messages for stack overflow
This commit is contained in:
Mark Shannon 2025-02-25 09:24:48 +00:00 committed by GitHub
parent 99088ab081
commit 014223649c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 1295 additions and 1482 deletions

View file

@ -612,12 +612,9 @@ PyObject_Print(PyObject *op, FILE *fp, int flags)
int write_error = 0;
if (PyErr_CheckSignals())
return -1;
#ifdef USE_STACKCHECK
if (PyOS_CheckStack()) {
PyErr_SetString(PyExc_MemoryError, "stack overflow");
if (_Py_EnterRecursiveCall(" printing an object")) {
return -1;
}
#endif
clearerr(fp); /* Clear any previous error condition */
if (op == NULL) {
Py_BEGIN_ALLOW_THREADS
@ -738,12 +735,6 @@ PyObject_Repr(PyObject *v)
PyObject *res;
if (PyErr_CheckSignals())
return NULL;
#ifdef USE_STACKCHECK
if (PyOS_CheckStack()) {
PyErr_SetString(PyExc_MemoryError, "stack overflow");
return NULL;
}
#endif
if (v == NULL)
return PyUnicode_FromString("<NULL>");
if (Py_TYPE(v)->tp_repr == NULL)
@ -786,12 +777,6 @@ PyObject_Str(PyObject *v)
PyObject *res;
if (PyErr_CheckSignals())
return NULL;
#ifdef USE_STACKCHECK
if (PyOS_CheckStack()) {
PyErr_SetString(PyExc_MemoryError, "stack overflow");
return NULL;
}
#endif
if (v == NULL)
return PyUnicode_FromString("<NULL>");
if (PyUnicode_CheckExact(v)) {
@ -2900,19 +2885,6 @@ _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
void
_PyTrash_thread_destroy_chain(PyThreadState *tstate)
{
/* We need to increase c_recursion_remaining here, otherwise,
_PyTrash_thread_destroy_chain will be called recursively
and then possibly crash. An example that may crash without
increase:
N = 500000 # need to be large enough
ob = object()
tups = [(ob,) for i in range(N)]
for i in range(49):
tups = [(tup,) for tup in tups]
del tups
*/
assert(tstate->c_recursion_remaining > Py_TRASHCAN_HEADROOM);
tstate->c_recursion_remaining--;
while (tstate->delete_later) {
PyObject *op = tstate->delete_later;
destructor dealloc = Py_TYPE(op)->tp_dealloc;
@ -2934,7 +2906,6 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
(*dealloc)(op);
}
tstate->c_recursion_remaining++;
}
void _Py_NO_RETURN