8000 GH-91079: Implement C stack limits using addresses, not counters. by markshannon · Pull Request #130007 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-91079: Implement C stack limits using addresses, not counters. #130007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 46 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
fc910a3
Hide that C recursion protection is implemented with a counter. There…
markshannon Feb 10, 2025
afeb866
Implement C recursion protection with limit pointers
markshannon Feb 11, 2025
22ca169
Use uintptr_t instead of char * to avoid warnings and UB
markshannon Feb 11, 2025
e8d8c4b
Merge branch 'main' into c-recursion-limit
markshannon Feb 11, 2025
b3638a5
Fix typo and update stable ABI
markshannon Feb 11, 2025
774efb5
Tweak AST test numbers
markshannon Feb 11, 2025
151c88f
Improve logic handling trial C stack overflow
markshannon Feb 11, 2025
350f8ec
Remove calls to PyOS_CheckStack
markshannon Feb 11, 2025
428c46a
Up the limits for recursion tests
markshannon Feb 11, 2025
a9be141
Use deeper stack for test
markshannon Feb 12, 2025
03fc52e
Remove exceeds_recursion_limit and get_c_recursion_limit. Use platfor…
markshannon Feb 12, 2025
afac1e6
Do fewer probes when growing stack limit
markshannon Feb 12, 2025
9da904d
Tweak depths
markshannon Feb 12, 2025
a802ff6
Merge branch 'main' into c-recursion-limit
markshannon Feb 12, 2025
dbcf6f0
Perform lazy initialization of c recursion check
markshannon Feb 12, 2025
2cc3287
Post merge fixup
markshannon Feb 12, 2025
e697926
Up depth again
markshannon Feb 12, 2025
f8a9143
Drop 'failing' depth
markshannon Feb 12, 2025
7d6d77f
Add news
markshannon Feb 12, 2025
31a83dc
Increase headroom
markshannon Feb 12, 2025
47c50aa
Update test
markshannon Feb 12, 2025
495c4ea
Tweak some more thresholds and tests
markshannon Feb 12, 2025
9e0cc67
Add stack protection to parser
markshannon Feb 13, 2025
857a7bb
Make tests more robust to low stacks
markshannon Feb 13, 2025
9c9326a
Improve error messages for stack overflow
markshannon Feb 13, 2025
75d3219
Merge branch 'main' into c-recursion-limit
markshannon Feb 13, 2025
3e41b46
Fix formatting
markshannon Feb 13, 2025
158401a
Halve size of WASI stack
markshannon Feb 13, 2025
c1eb229
Reduce webassembly 'stack size' by 10
markshannon Feb 13, 2025
7407d2b
Halve size of WASI stack, again
markshannon Feb 13, 2025
978b5e7
Change WASI stack back to 100k
markshannon Feb 13, 2025
7b36f59
Add many skip tests for WASI due to stack issues
markshannon Feb 13, 2025
5e5db03
Probe all pages when extending stack limits
markshannon Feb 14, 2025
82173ed
Fix compiler warnings
markshannon Feb 14, 2025
64cfd86
Use GetCurrentThreadStackLimits instead of probing with alloca
markshannon Feb 14, 2025
e52137f
Refactor a bit
markshannon Feb 14, 2025
704c336
Merge branch 'main' into c-recursion-limit
markshannon Feb 14, 2025
21366c3
Fix logic error in test
markshannon Feb 17, 2025
7761d31
Make ABI function needed for Py_TRASHCAN_BEGIN private
markshannon Feb 17, 2025
b067e3e
Move new fields to _PyThreadStateImpl to avoid any potential API brea…
markshannon Feb 17, 2025
b0e695f
Fix missing cast
markshannon Feb 17, 2025
c4cd68f
yet another missing _
markshannon Feb 17, 2025
9654790
Tidy up a bit
markshannon Feb 18, 2025
2cef96f
Restore use of exceeds_recursion_limit
markshannon Feb 18, 2025
c5d8a40
Address remaining review comments
markshannon Feb 18, 2025
33e11c8
Merge branch 'main' into c-recursion-limit
markshannon Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve error messages for stack overflow
  • Loading branch information
markshannon committed Feb 13, 2025
commit 9c9326a25eac549ce15ffe41e3d09c1f2cfab6d9
44 changes: 1 addition & 43 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct _ts {
int py_recursion_limit;

// These are addresses, but we need to convert to ints to avoid UB.
uintptr_t c_stack_top;
uintptr_t c_stack_soft_limit;
uintptr_t c_stack_hard_limit;
int recursion_headroom; /* Allow 50 more calls to handle any errors. */
Expand Down Expand Up @@ -204,48 +205,6 @@ struct _ts {
PyObject *threading_local_sentinel;
};


#if defined(__s390x__)
# define Py_C_STACK_SIZE 320000
#elif defined(_WIN32) && defined(_M_ARM64)
# define Py_C_STACK_SIZE 400000
#elif defined(_WIN32)
# define Py_C_STACK_SIZE 1200000
#elif defined(__ANDROID__)
// On an ARM64 emulator, API level 34 was OK with 10000, but API level 21
// crashed in test_compiler_recursion_limit.
# define Py_C_STACK_SIZE 1200000
#elif defined(__sparc__)
// test_descr crashed on sparc64 with >7000 but let's keep a margin of error.
# define Py_C_STACK_SIZE 1600000
#elif defined(__wasi__)
// Based on wasmtime 16.
# define Py_C_STACK_SIZE 2000000
#elif defined(__hppa__) || defined(__powerpc64__)
// test_descr crashed with >8000 but let's keep a margin of error.
# define Py_C_STACK_SIZE 2000000
#else
// This value is duplicated in Lib/test/support/__init__.py
# define Py_C_STACK_SIZE 5000000
#endif


#ifdef Py_DEBUG
// A debug build is likely built with low optimization level which implies
// higher stack memory usage than a release build: use a lower limit.
# if defined(__has_feature) /* Clang */
// Clang debug builds use a lot of stack space
# define Py_C_RECURSION_LIMIT (Py_C_STACK_SIZE / 4000)
# else
# define Py_C_RECURSION_LIMIT (Py_C_STACK_SIZE / 2000)
# endif
#elif defined(_Py_ADDRESS_SANITIZER)
# define Py_C_RECURSION_LIMIT (Py_C_STACK_SIZE / 600)
#else
# define Py_C_RECURSION_LIMIT (Py_C_STACK_SIZE / 300)
#endif


/* other API */

/* Similar to PyThreadState_Get(), but don't issue a fatal error
Expand All @@ -259,7 +218,6 @@ _PyThreadState_UncheckedGet(void)
return PyThreadState_GetUnchecked();
}


// Disable tracing and profiling.
PyAPI_FUNC(void) PyThreadState_EnterTracing(PyThreadState *tstate);

Expand Down
1 change: 0 additions & 1 deletion Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3213,7 +3213,6 @@ PyInit__testcapi(void)
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);

PyModule_AddIntConstant(m, "the_number_three", 3);
PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);
PyModule_AddObject(m, "INT32_MIN", PyLong_FromInt32(INT32_MIN));
PyModule_AddObject(m, "INT32_MAX", PyLong_FromInt32(INT32_MAX));
PyModule_AddObject(m, "UINT32_MAX", PyLong_FromUInt32(UINT32_MAX));
Expand Down
31 changes: 29 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,33 @@ _Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
}
}

#if defined(__s390x__)
# define Py_C_STACK_SIZE 320000
#elif defined(_WIN32) && defined(_M_ARM64)
# define Py_C_STACK_SIZE 400000
#elif defined(_WIN32)
# define Py_C_STACK_SIZE 1200000
#elif defined(__ANDROID__)
# define Py_C_STACK_SIZE 1200000
#elif defined(__sparc__)
// test_descr crashed on sparc64 with >7000 but let's keep a margin of error.
# define Py_C_STACK_SIZE 1600000
#elif defined(__wasi__)
// Based on wasmtime 16.
# define Py_C_STACK_SIZE 2000000
#elif defined(__hppa__) || defined(__powerpc64__)
// test_descr crashed with >8000 but let's keep a margin of error.
# define Py_C_STACK_SIZE 2000000
#else
# define Py_C_STACK_SIZE 5000000
#endif

void
_Py_InitializeRecursionCheck(PyThreadState *tstate)
{
char here;
uintptr_t here_addr = (uintptr_t)&here;
tstate->c_stack_top = here_addr;
#ifdef USE_STACKCHECK
if (_PyOS_CheckStack(PYOS_STACK_MARGIN * 2) == 0) {
tstate->c_stack_soft_limit = here_addr - PYOS_STACK_MARGIN_BYTES;
Expand Down Expand Up @@ -364,15 +386,20 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
assert(tstate->c_stack_hard_limit != 0);
if (here_addr < tstate->c_stack_hard_limit) {
/* Overflowing while handling an overflow. Give up. */
Py_FatalError("Cannot recover from stack overflow.");
int kbytes_used = (tstate->c_stack_top - here_addr)/1024;
char buffer[80];
snprintf(buffer, 80, "Unrecoverable stack overflow (used %dkb)%s", kbytes_used, where);
Py_FatalError(buffer);
}
if (tstate->recursion_headroom) {
return 0;
}
else {
int kbytes_used = (tstate->c_stack_top - here_addr)/1024;
tstate->recursion_headroom++;
_PyErr_Format(tstate, PyExc_RecursionError,
"maximum recursion depth exceeded%s",
"Stack overflow (used %dkb)S%s",
kbytes_used,
where);
tstate->recursion_headroom--;
return -1;
Expand Down
0