Skip to content

Commit b067e3e

Browse files
committed
Move new fields to _PyThreadStateImpl to avoid any potential API breakage
1 parent 7761d31 commit b067e3e

File tree

6 files changed

+42
-30
lines changed

6 files changed

+42
-30
lines changed

Include/cpython/pystate.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,7 @@ struct _ts {
112112
int py_recursion_remaining;
113113
int py_recursion_limit;
114114

115-
// These are addresses, but we need to convert to ints to avoid UB.
116-
uintptr_t c_stack_top;
117-
uintptr_t c_stack_soft_limit;
118-
uintptr_t c_stack_hard_limit;
115+
int c_recursion_remaining;
119116
int recursion_headroom; /* Allow 50 more calls to handle any errors. */
120117

121118
/* 'tracing' keeps track of the execution depth when tracing/profiling.
@@ -205,6 +202,8 @@ struct _ts {
205202
PyObject *threading_local_sentinel;
206203
};
207204

205+
# define Py_C_RECURSION_LIMIT 5000
206+
208207
/* other API */
209208

210209
/* Similar to PyThreadState_Get(), but don't issue a fatal error

Include/internal/pycore_ceval.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ extern void _PyEval_DeactivateOpCache(void);
196196
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
197197
char here;
198198
uintptr_t here_addr = (uintptr_t)&here;
199-
return here_addr < tstate->c_stack_soft_limit;
199+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
200+
return here_addr < _tstate->c_stack_soft_limit;
200201
}
201202

202203
// Export for '_json' shared extension, used via _Py_EnterRecursiveCall()
@@ -227,13 +228,14 @@ PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
227228
static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
228229
char here;
229230
uintptr_t here_addr = (uintptr_t)&here;
230-
if (here_addr > tstate->c_stack_soft_limit) {
231+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
232+
if (here_addr > _tstate->c_stack_soft_limit) {
231233
return 0;
232234
}
233-
if (tstate->c_stack_hard_limit == 0) {
235+
if (_tstate->c_stack_hard_limit == 0) {
234236
_Py_InitializeRecursionLimits(tstate);
235237
}
236-
return here_addr <= tstate->c_stack_soft_limit;
238+
return here_addr <= _tstate->c_stack_soft_limit;
237239
}
238240

239241
static inline void _Py_LeaveRecursiveCall(void) {

Include/internal/pycore_tstate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ typedef struct _PyThreadStateImpl {
2121
// semi-public fields are in PyThreadState.
2222
PyThreadState base;
2323

24+
// These are addresses, but we need to convert to ints to avoid UB.
25+
uintptr_t c_stack_top;
26+
uintptr_t c_stack_soft_limit;
27+
uintptr_t c_stack_hard_limit;
28+
2429
PyObject *asyncio_running_loop; // Strong reference
2530
PyObject *asyncio_running_task; // Strong reference
2631

Modules/_testinternalcapi.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ get_c_recursion_remaining(PyObject *self, PyObject *Py_UNUSED(args))
117117
PyThreadState *tstate = _PyThreadState_GET();
118118
char here;
119119
uintptr_t here_addr = (uintptr_t)&here;
120-
int remaining = (int)((here_addr - tstate->c_stack_soft_limit)/PYOS_STACK_MARGIN_BYTES * 50);
120+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
121+
int remaining = (int)((here_addr - _tstate->c_stack_soft_limit)/PYOS_STACK_MARGIN_BYTES * 50);
121122
return PyLong_FromLong(remaining);
122123
}
123124

Python/ceval.c

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -309,21 +309,23 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count)
309309
{
310310
char here;
311311
uintptr_t here_addr = (uintptr_t)&here;
312-
if (here_addr > tstate->c_stack_soft_limit + margin_count * PYOS_STACK_MARGIN_BYTES) {
312+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
313+
if (here_addr > _tstate->c_stack_soft_limit + margin_count * PYOS_STACK_MARGIN_BYTES) {
313314
return 0;
314315
}
315-
if (tstate->c_stack_hard_limit == 0) {
316+
if (_tstate->c_stack_hard_limit == 0) {
316317
_Py_InitializeRecursionLimits(tstate);
317318
}
318-
return here_addr <= tstate->c_stack_soft_limit + margin_count * PYOS_STACK_MARGIN_BYTES;
319+
return here_addr <= _tstate->c_stack_soft_limit + margin_count * PYOS_STACK_MARGIN_BYTES;
319320
}
320321

321322
void
322323
_Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
323324
{
324325
char here;
325326
uintptr_t here_addr = (uintptr_t)&here;
326-
if (here_addr < tstate->c_stack_hard_limit) {
327+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
328+
if (here_addr < _tstate->c_stack_hard_limit) {
327329
Py_FatalError("Unchecked stack overflow.");
328330
}
329331
}
@@ -350,20 +352,21 @@ _Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
350352
void
351353
_Py_InitializeRecursionLimits(PyThreadState *tstate)
352354
{
355+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
353356
#ifdef WIN32
354357
ULONG_PTR low, high;
355358
GetCurrentThreadStackLimits(&low, &high);
356-
tstate->c_stack_top = (uintptr_t)high;
359+
_tstate->c_stack_top = (uintptr_t)high;
357360
ULONG guarantee = 0;
358361
SetThreadStackGuarantee(&guarantee);
359-
tstate->c_stack_hard_limit = ((uintptr_t)low) + guarantee + PYOS_STACK_MARGIN_BYTES;
360-
tstate->c_stack_soft_limit = tstate->c_stack_hard_limit + PYOS_STACK_MARGIN_BYTES;
362+
_tstate->c_stack_hard_limit = ((uintptr_t)low) + guarantee + PYOS_STACK_MARGIN_BYTES;
363+
_tstate->c_stack_soft_limit = tstate->c_stack_hard_limit + PYOS_STACK_MARGIN_BYTES;
361364
#else
362365
char here;
363366
uintptr_t here_addr = (uintptr_t)&here;
364-
tstate->c_stack_top = _Py_SIZE_ROUND_UP(here_addr, 4096);
365-
tstate->c_stack_soft_limit = tstate->c_stack_top - Py_C_STACK_SIZE;
366-
tstate->c_stack_hard_limit = tstate->c_stack_top - (Py_C_STACK_SIZE + PYOS_STACK_MARGIN_BYTES);
367+
_tstate->c_stack_top = _Py_SIZE_ROUND_UP(here_addr, 4096);
368+
_tstate->c_stack_soft_limit = _tstate->c_stack_top - Py_C_STACK_SIZE;
369+
_tstate->c_stack_hard_limit = _tstate->c_stack_top - (Py_C_STACK_SIZE + PYOS_STACK_MARGIN_BYTES);
367370
#endif
368371
}
369372

@@ -372,19 +375,20 @@ _Py_InitializeRecursionLimits(PyThreadState *tstate)
372375
int
373376
_Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
374377
{
378+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
375379
char here;
376380
uintptr_t here_addr = (uintptr_t)&here;
377-
assert(tstate->c_stack_soft_limit != 0);
378-
if (tstate->c_stack_hard_limit == 0) {
381+
assert(_tstate->c_stack_soft_limit != 0);
382+
if (_tstate->c_stack_hard_limit == 0) {
379383
_Py_InitializeRecursionLimits(tstate);
380384
}
381-
if (here_addr >= tstate->c_stack_soft_limit) {
385+
if (here_addr >= _tstate->c_stack_soft_limit) {
382386
return 0;
383387
}
384-
assert(tstate->c_stack_hard_limit != 0);
385-
if (here_addr < tstate->c_stack_hard_limit) {
388+
assert(_tstate->c_stack_hard_limit != 0);
389+
if (here_addr < _tstate->c_stack_hard_limit) {
386390
/* Overflowing while handling an overflow. Give up. */
387-
int kbytes_used = (int)(tstate->c_stack_top - here_addr)/1024;
391+
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
388392
char buffer[80];
389393
snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where);
390394
Py_FatalError(buffer);
@@ -393,7 +397,7 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
393397
return 0;
394398
}
395399
else {
396-
int kbytes_used = (int)(tstate->c_stack_top - here_addr)/1024;
400+
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
397401
tstate->recursion_headroom++;
398402
_PyErr_Format(tstate, PyExc_RecursionError,
399403
"Stack overflow (used %d kB)%s",

Python/pystate.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,10 +1492,7 @@ init_threadstate(_PyThreadStateImpl *_tstate,
14921492

14931493
tstate->py_recursion_limit = interp->ceval.recursion_limit;
14941494
tstate->py_recursion_remaining = interp->ceval.recursion_limit;
1495-
tstate->c_stack_soft_limit = UINTPTR_MAX;
1496-
tstate->c_stack_top = 0;
1497-
tstate->c_stack_hard_limit = 0;
1498-
1495+
tstate->c_recursion_remaining = 2;
14991496
tstate->exc_info = &tstate->exc_state;
15001497

15011498
// PyGILState_Release must not try to delete this thread state.
@@ -1510,6 +1507,10 @@ init_threadstate(_PyThreadStateImpl *_tstate,
15101507
tstate->previous_executor = NULL;
15111508
tstate->dict_global_version = 0;
15121509

1510+
_tstate->c_stack_soft_limit = UINTPTR_MAX;
1511+
_tstate->c_stack_top = 0;
1512+
_tstate->c_stack_hard_limit = 0;
1513+
15131514
_tstate->asyncio_running_loop = NULL;
15141515
_tstate->asyncio_running_task = NULL;
15151516

0 commit comments

Comments
 (0)