Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb99db92

Browse files
vstinnerencukou
andauthored
gh-139653: Add PyUnstable_ThreadState_SetStackProtection() (#139668)
Add PyUnstable_ThreadState_SetStackProtection() andPyUnstable_ThreadState_ResetStackProtection() functionsto set the stack base address and stack size of a Pythonthread state.Co-authored-by: Petr Viktorin <encukou@gmail.com>
1 parentd7862e9 commitb99db92

File tree

10 files changed

+199
-7
lines changed

10 files changed

+199
-7
lines changed

‎Doc/c-api/exceptions.rst‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,9 @@ because the :ref:`call protocol <call>` takes care of recursion handling.
976976
be concatenated to the:exc:`RecursionError` message caused by the recursion
977977
depth limit.
978978
979+
..seealso::
980+
The:c:func:`PyUnstable_ThreadState_SetStackProtection` function.
981+
979982
..versionchanged::3.9
980983
This function is now also available in the:ref:`limited API<limited-c-api>`.
981984

‎Doc/c-api/init.rst‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,43 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
13661366
.. versionadded:: 3.11
13671367
13681368
1369+
.. c:function:: int PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, void *stack_start_addr, size_t stack_size)
1370+
1371+
Set the stack protection start address and stack protection size
1372+
of a Python thread state.
1373+
1374+
On success, return ``0``.
1375+
On failure, set an exception and return ``-1``.
1376+
1377+
CPython implements :ref:`recursion control <recursion>` for C code by raising
1378+
:py:exc:`RecursionError` when it notices that the machine execution stack is close
1379+
to overflow. See for example the :c:func:`Py_EnterRecursiveCall` function.
1380+
For this, it needs to know the location of the current thread's stack, which it
1381+
normally gets from the operating system.
1382+
When the stack is changed, for example using context switching techniques like the
1383+
Boost library's ``boost::context``, you must call
1384+
:c:func:`~PyUnstable_ThreadState_SetStackProtection` to inform CPython of the change.
1385+
1386+
Call :c:func:`~PyUnstable_ThreadState_SetStackProtection` either before
1387+
or after changing the stack.
1388+
Do not call any other Python C API between the call and the stack
1389+
change.
1390+
1391+
See :c:func:`PyUnstable_ThreadState_ResetStackProtection` for undoing this operation.
1392+
1393+
.. versionadded:: next
1394+
1395+
1396+
.. c:function:: void PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
1397+
1398+
Reset the stack protection start address and stack protection size
1399+
of a Python thread state to the operating system defaults.
1400+
1401+
See :c:func:`PyUnstable_ThreadState_SetStackProtection` for an explanation.
1402+
1403+
.. versionadded:: next
1404+
1405+
13691406
.. c:function:: PyInterpreterState* PyInterpreterState_Get(void)
13701407
13711408
Get the current interpreter.

‎Doc/whatsnew/3.15.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,12 @@ New features
10661066
* Add:c:func:`PyTuple_FromArray` to create a:class:`tuple` from an array.
10671067
(Contributed by Victor Stinner in:gh:`111489`.)
10681068

1069+
* Add:c:func:`PyUnstable_ThreadState_SetStackProtection` and
1070+
:c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set
1071+
the stack protection base address and stack protection size of a Python
1072+
thread state.
1073+
(Contributed by Victor Stinner in:gh:`139653`.)
1074+
10691075

10701076
Changed C APIs
10711077
--------------

‎Include/cpython/pystate.h‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,18 @@ PyAPI_FUNC(int) PyGILState_Check(void);
276276
*/
277277
PyAPI_FUNC(PyObject*)_PyThread_CurrentFrames(void);
278278

279+
// Set the stack protection start address and stack protection size
280+
// of a Python thread state
281+
PyAPI_FUNC(int)PyUnstable_ThreadState_SetStackProtection(
282+
PyThreadState*tstate,
283+
void*stack_start_addr,// Stack start address
284+
size_tstack_size);// Stack size (in bytes)
285+
286+
// Reset the stack protection start address and stack protection size
287+
// of a Python thread state
288+
PyAPI_FUNC(void)PyUnstable_ThreadState_ResetStackProtection(
289+
PyThreadState*tstate);
290+
279291
/* Routines for advanced debuggers, requested by David Beazley.
280292
Don't use unless you know what you are doing! */
281293
PyAPI_FUNC(PyInterpreterState*)PyInterpreterState_Main(void);

‎Include/internal/pycore_pythonrun.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ extern PyObject * _Py_CompileStringObjectWithModule(
6060
# define_PyOS_STACK_MARGIN_SHIFT (_PyOS_LOG2_STACK_MARGIN + 2)
6161
#endif
6262

63+
#ifdef_Py_THREAD_SANITIZER
64+
# define_PyOS_MIN_STACK_SIZE (_PyOS_STACK_MARGIN_BYTES * 6)
65+
#else
66+
# define_PyOS_MIN_STACK_SIZE (_PyOS_STACK_MARGIN_BYTES * 3)
67+
#endif
68+
6369

6470
#ifdef__cplusplus
6571
}

‎Include/internal/pycore_tstate.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ typedef struct _PyThreadStateImpl {
3737
uintptr_tc_stack_soft_limit;
3838
uintptr_tc_stack_hard_limit;
3939

40+
// PyUnstable_ThreadState_ResetStackProtection() values
41+
uintptr_tc_stack_init_base;
42+
uintptr_tc_stack_init_top;
43+
4044
PyObject*asyncio_running_loop;// Strong reference
4145
PyObject*asyncio_running_task;// Strong reference
4246

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add:c:func:`PyUnstable_ThreadState_SetStackProtection` and
2+
:c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set the
3+
stack protection base address and stack protection size of a Python thread
4+
state. Patch by Victor Stinner.

‎Modules/_testinternalcapi.c‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,6 +2446,58 @@ module_get_gc_hooks(PyObject *self, PyObject *arg)
24462446
returnresult;
24472447
}
24482448

2449+
2450+
staticvoid
2451+
check_threadstate_set_stack_protection(PyThreadState*tstate,
2452+
void*start,size_tsize)
2453+
{
2454+
assert(PyUnstable_ThreadState_SetStackProtection(tstate,start,size)==0);
2455+
assert(!PyErr_Occurred());
2456+
2457+
_PyThreadStateImpl*ts= (_PyThreadStateImpl*)tstate;
2458+
assert(ts->c_stack_top== (uintptr_t)start+size);
2459+
assert(ts->c_stack_hard_limit <=ts->c_stack_soft_limit);
2460+
assert(ts->c_stack_soft_limit<ts->c_stack_top);
2461+
}
2462+
2463+
2464+
staticPyObject*
2465+
test_threadstate_set_stack_protection(PyObject*self,PyObject*Py_UNUSED(args))
2466+
{
2467+
PyThreadState*tstate=PyThreadState_GET();
2468+
_PyThreadStateImpl*ts= (_PyThreadStateImpl*)tstate;
2469+
assert(!PyErr_Occurred());
2470+
2471+
uintptr_tinit_base=ts->c_stack_init_base;
2472+
size_tinit_top=ts->c_stack_init_top;
2473+
2474+
// Test the minimum stack size
2475+
size_tsize=_PyOS_MIN_STACK_SIZE;
2476+
void*start= (void*)(_Py_get_machine_stack_pointer()-size);
2477+
check_threadstate_set_stack_protection(tstate,start,size);
2478+
2479+
// Test a larger size
2480+
size=7654321;
2481+
assert(size>_PyOS_MIN_STACK_SIZE);
2482+
start= (void*)(_Py_get_machine_stack_pointer()-size);
2483+
check_threadstate_set_stack_protection(tstate,start,size);
2484+
2485+
// Test invalid size (too small)
2486+
size=5;
2487+
start= (void*)(_Py_get_machine_stack_pointer()-size);
2488+
assert(PyUnstable_ThreadState_SetStackProtection(tstate,start,size)==-1);
2489+
assert(PyErr_ExceptionMatches(PyExc_ValueError));
2490+
PyErr_Clear();
2491+
2492+
// Test PyUnstable_ThreadState_ResetStackProtection()
2493+
PyUnstable_ThreadState_ResetStackProtection(tstate);
2494+
assert(ts->c_stack_init_base==init_base);
2495+
assert(ts->c_stack_init_top==init_top);
2496+
2497+
Py_RETURN_NONE;
2498+
}
2499+
2500+
24492501
staticPyMethodDefmodule_functions[]= {
24502502
{"get_configs",get_configs,METH_NOARGS},
24512503
{"get_recursion_depth",get_recursion_depth,METH_NOARGS},
@@ -2556,6 +2608,8 @@ static PyMethodDef module_functions[] = {
25562608
{"simple_pending_call",simple_pending_call,METH_O},
25572609
{"set_vectorcall_nop",set_vectorcall_nop,METH_O},
25582610
{"module_get_gc_hooks",module_get_gc_hooks,METH_O},
2611+
{"test_threadstate_set_stack_protection",
2612+
test_threadstate_set_stack_protection,METH_NOARGS},
25592613
{NULL,NULL}/* sentinel */
25602614
};
25612615

‎Python/ceval.c‎

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ int pthread_attr_destroy(pthread_attr_t *a)
443443
#endif
444444

445445
staticvoid
446-
hardware_stack_limits(uintptr_t*top,uintptr_t*base)
446+
hardware_stack_limits(uintptr_t*base,uintptr_t*top)
447447
{
448448
#ifdefWIN32
449449
ULONG_PTRlow,high;
@@ -486,23 +486,86 @@ hardware_stack_limits(uintptr_t *top, uintptr_t *base)
486486
#endif
487487
}
488488

489-
void
490-
_Py_InitializeRecursionLimits(PyThreadState*tstate)
489+
staticvoid
490+
tstate_set_stack(PyThreadState*tstate,
491+
uintptr_tbase,uintptr_ttop)
491492
{
492-
uintptr_ttop;
493-
uintptr_tbase;
494-
hardware_stack_limits(&top,&base);
493+
assert(base<top);
494+
assert((top-base) >=_PyOS_MIN_STACK_SIZE);
495+
495496
#ifdef_Py_THREAD_SANITIZER
496497
// Thread sanitizer crashes if we use more than half the stack.
497498
uintptr_tstacksize=top-base;
498-
base+=stacksize/2;
499+
base+=stacksize /2;
499500
#endif
500501
_PyThreadStateImpl*_tstate= (_PyThreadStateImpl*)tstate;
501502
_tstate->c_stack_top=top;
502503
_tstate->c_stack_hard_limit=base+_PyOS_STACK_MARGIN_BYTES;
503504
_tstate->c_stack_soft_limit=base+_PyOS_STACK_MARGIN_BYTES*2;
505+
506+
#ifndefNDEBUG
507+
// Sanity checks
508+
_PyThreadStateImpl*ts= (_PyThreadStateImpl*)tstate;
509+
assert(ts->c_stack_hard_limit <=ts->c_stack_soft_limit);
510+
assert(ts->c_stack_soft_limit<ts->c_stack_top);
511+
#endif
512+
}
513+
514+
515+
void
516+
_Py_InitializeRecursionLimits(PyThreadState*tstate)
517+
{
518+
uintptr_tbase,top;
519+
hardware_stack_limits(&base,&top);
520+
assert(top!=0);
521+
522+
tstate_set_stack(tstate,base,top);
523+
_PyThreadStateImpl*ts= (_PyThreadStateImpl*)tstate;
524+
ts->c_stack_init_base=base;
525+
ts->c_stack_init_top=top;
526+
527+
// Test the stack pointer
528+
#if !defined(NDEBUG)&& !defined(__wasi__)
529+
uintptr_there_addr=_Py_get_machine_stack_pointer();
530+
assert(ts->c_stack_soft_limit<here_addr);
531+
assert(here_addr<ts->c_stack_top);
532+
#endif
533+
}
534+
535+
536+
int
537+
PyUnstable_ThreadState_SetStackProtection(PyThreadState*tstate,
538+
void*stack_start_addr,size_tstack_size)
539+
{
540+
if (stack_size<_PyOS_MIN_STACK_SIZE) {
541+
PyErr_Format(PyExc_ValueError,
542+
"stack_size must be at least %zu bytes",
543+
_PyOS_MIN_STACK_SIZE);
544+
return-1;
545+
}
546+
547+
uintptr_tbase= (uintptr_t)stack_start_addr;
548+
uintptr_ttop=base+stack_size;
549+
tstate_set_stack(tstate,base,top);
550+
return0;
504551
}
505552

553+
554+
void
555+
PyUnstable_ThreadState_ResetStackProtection(PyThreadState*tstate)
556+
{
557+
_PyThreadStateImpl*ts= (_PyThreadStateImpl*)tstate;
558+
if (ts->c_stack_init_top!=0) {
559+
tstate_set_stack(tstate,
560+
ts->c_stack_init_base,
561+
ts->c_stack_init_top);
562+
return;
563+
}
564+
565+
_Py_InitializeRecursionLimits(tstate);
566+
}
567+
568+
506569
/* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
507570
if the recursion_depth reaches recursion_limit. */
508571
int

‎Python/pystate.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,9 @@ init_threadstate(_PyThreadStateImpl *_tstate,
14951495
_tstate->c_stack_top=0;
14961496
_tstate->c_stack_hard_limit=0;
14971497

1498+
_tstate->c_stack_init_base=0;
1499+
_tstate->c_stack_init_top=0;
1500+
14981501
_tstate->asyncio_running_loop=NULL;
14991502
_tstate->asyncio_running_task=NULL;
15001503

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp