mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									99088ab081
								
							
						
					
					
						commit
						014223649c
					
				
					 58 changed files with 1295 additions and 1482 deletions
				
			
		
							
								
								
									
										138
									
								
								Python/ceval.c
									
										
									
									
									
								
							
							
						
						
									
										138
									
								
								Python/ceval.c
									
										
									
									
									
								
							|  | @ -304,36 +304,122 @@ Py_SetRecursionLimit(int new_limit) | |||
|     _PyEval_StartTheWorld(interp); | ||||
| } | ||||
| 
 | ||||
| int | ||||
| _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count) | ||||
| { | ||||
|     uintptr_t here_addr = _Py_get_machine_stack_pointer(); | ||||
|     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; | ||||
|     if (here_addr > _tstate->c_stack_soft_limit + margin_count * PYOS_STACK_MARGIN_BYTES) { | ||||
|         return 0; | ||||
|     } | ||||
|     if (_tstate->c_stack_hard_limit == 0) { | ||||
|         _Py_InitializeRecursionLimits(tstate); | ||||
|     } | ||||
|     return here_addr <= _tstate->c_stack_soft_limit + margin_count * PYOS_STACK_MARGIN_BYTES; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| _Py_EnterRecursiveCallUnchecked(PyThreadState *tstate) | ||||
| { | ||||
|     uintptr_t here_addr = _Py_get_machine_stack_pointer(); | ||||
|     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; | ||||
|     if (here_addr < _tstate->c_stack_hard_limit) { | ||||
|         Py_FatalError("Unchecked stack overflow."); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #if defined(__s390x__) | ||||
| #  define Py_C_STACK_SIZE 320000 | ||||
| #elif defined(_WIN32) | ||||
|    // Don't define Py_C_STACK_SIZE, ask the O/S
 | ||||
| #elif defined(__ANDROID__) | ||||
| #  define Py_C_STACK_SIZE 1200000 | ||||
| #elif defined(__sparc__) | ||||
| #  define Py_C_STACK_SIZE 1600000 | ||||
| #elif defined(__wasi__) | ||||
|    /* Web assembly has two stacks, so this isn't really the stack depth */ | ||||
| #  define Py_C_STACK_SIZE 80000 | ||||
| #elif defined(__hppa__) || defined(__powerpc64__) | ||||
| #  define Py_C_STACK_SIZE 2000000 | ||||
| #else | ||||
| #  define Py_C_STACK_SIZE 4000000 | ||||
| #endif | ||||
| 
 | ||||
| void | ||||
| _Py_InitializeRecursionLimits(PyThreadState *tstate) | ||||
| { | ||||
|     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; | ||||
| #ifdef WIN32 | ||||
|     ULONG_PTR low, high; | ||||
|     GetCurrentThreadStackLimits(&low, &high); | ||||
|     _tstate->c_stack_top = (uintptr_t)high; | ||||
|     ULONG guarantee = 0; | ||||
|     SetThreadStackGuarantee(&guarantee); | ||||
|     _tstate->c_stack_hard_limit = ((uintptr_t)low) + guarantee + PYOS_STACK_MARGIN_BYTES; | ||||
|     _tstate->c_stack_soft_limit = _tstate->c_stack_hard_limit + PYOS_STACK_MARGIN_BYTES; | ||||
| #else | ||||
|     uintptr_t here_addr = _Py_get_machine_stack_pointer(); | ||||
| #  if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) | ||||
|     size_t stack_size, guard_size; | ||||
|     void *stack_addr; | ||||
|     pthread_attr_t attr; | ||||
|     int err = pthread_getattr_np(pthread_self(), &attr); | ||||
|     if (err == 0) { | ||||
|         err = pthread_attr_getguardsize(&attr, &guard_size); | ||||
|         err |= pthread_attr_getstack(&attr, &stack_addr, &stack_size); | ||||
|         err |= pthread_attr_destroy(&attr); | ||||
|     } | ||||
|     if (err == 0) { | ||||
|         uintptr_t base = ((uintptr_t)stack_addr) + guard_size; | ||||
|         _tstate->c_stack_top = base + stack_size; | ||||
|         _tstate->c_stack_soft_limit = base + PYOS_STACK_MARGIN_BYTES * 2; | ||||
|         _tstate->c_stack_hard_limit = base + PYOS_STACK_MARGIN_BYTES; | ||||
|         assert(_tstate->c_stack_soft_limit < here_addr); | ||||
|         assert(here_addr < _tstate->c_stack_top); | ||||
|         return; | ||||
|     } | ||||
| #  endif | ||||
|     _tstate->c_stack_top = _Py_SIZE_ROUND_UP(here_addr, 4096); | ||||
|     _tstate->c_stack_soft_limit = _tstate->c_stack_top - Py_C_STACK_SIZE; | ||||
|     _tstate->c_stack_hard_limit = _tstate->c_stack_top - (Py_C_STACK_SIZE + PYOS_STACK_MARGIN_BYTES); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
 | ||||
|    if the recursion_depth reaches recursion_limit. */ | ||||
| int | ||||
| _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) | ||||
| { | ||||
| #ifdef USE_STACKCHECK | ||||
|     if (PyOS_CheckStack()) { | ||||
|         ++tstate->c_recursion_remaining; | ||||
|         _PyErr_SetString(tstate, PyExc_MemoryError, "Stack overflow"); | ||||
|         return -1; | ||||
|     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; | ||||
|     uintptr_t here_addr = _Py_get_machine_stack_pointer(); | ||||
|     assert(_tstate->c_stack_soft_limit != 0); | ||||
|     if (_tstate->c_stack_hard_limit == 0) { | ||||
|         _Py_InitializeRecursionLimits(tstate); | ||||
|     } | ||||
|     if (here_addr >= _tstate->c_stack_soft_limit) { | ||||
|         return 0; | ||||
|     } | ||||
|     assert(_tstate->c_stack_hard_limit != 0); | ||||
|     if (here_addr < _tstate->c_stack_hard_limit) { | ||||
|         /* Overflowing while handling an overflow. Give up. */ | ||||
|         int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024; | ||||
|         char buffer[80]; | ||||
|         snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where); | ||||
|         Py_FatalError(buffer); | ||||
|     } | ||||
| #endif | ||||
|     if (tstate->recursion_headroom) { | ||||
|         if (tstate->c_recursion_remaining < -50) { | ||||
|             /* Overflowing while handling an overflow. Give up. */ | ||||
|             Py_FatalError("Cannot recover from stack overflow."); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|     else { | ||||
|         if (tstate->c_recursion_remaining <= 0) { | ||||
|             tstate->recursion_headroom++; | ||||
|             _PyErr_Format(tstate, PyExc_RecursionError, | ||||
|                         "maximum recursion depth exceeded%s", | ||||
|                         where); | ||||
|             tstate->recursion_headroom--; | ||||
|             ++tstate->c_recursion_remaining; | ||||
|             return -1; | ||||
|         } | ||||
|         int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024; | ||||
|         tstate->recursion_headroom++; | ||||
|         _PyErr_Format(tstate, PyExc_RecursionError, | ||||
|                     "Stack overflow (used %d kB)%s", | ||||
|                     kbytes_used, | ||||
|                     where); | ||||
|         tstate->recursion_headroom--; | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -761,11 +847,6 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* _PyEval_EvalFrameDefault() is a *big* function,
 | ||||
|  * so consume 3 units of C stack */ | ||||
| #define PY_EVAL_C_STACK_UNITS 2 | ||||
| 
 | ||||
| 
 | ||||
| /* _PyEval_EvalFrameDefault is too large to optimize for speed with PGO on MSVC.
 | ||||
|  */ | ||||
| #if (defined(_MSC_VER) && \ | ||||
|  | @ -838,8 +919,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
|     frame->previous = &entry_frame; | ||||
|     tstate->current_frame = frame; | ||||
| 
 | ||||
|     tstate->c_recursion_remaining -= (PY_EVAL_C_STACK_UNITS - 1); | ||||
| 
 | ||||
|     /* support for generator.throw() */ | ||||
|     if (throwflag) { | ||||
|         if (_Py_EnterRecursivePy(tstate)) { | ||||
|  | @ -998,7 +1077,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
|     assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); | ||||
|     /* Restore previous frame and exit */ | ||||
|     tstate->current_frame = frame->previous; | ||||
|     tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
|  | @ -1562,11 +1640,9 @@ clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) | |||
|     // _PyThreadState_PopFrame, since f_code is already cleared at that point:
 | ||||
|     assert((PyObject **)frame + _PyFrame_GetCode(frame)->co_framesize == | ||||
|         tstate->datastack_top); | ||||
|     tstate->c_recursion_remaining--; | ||||
|     assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); | ||||
|     _PyFrame_ClearExceptCode(frame); | ||||
|     PyStackRef_CLEAR(frame->f_executable); | ||||
|     tstate->c_recursion_remaining++; | ||||
|     _PyThreadState_PopFrame(tstate, frame); | ||||
| } | ||||
| 
 | ||||
|  | @ -1579,11 +1655,9 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) | |||
|     assert(tstate->exc_info == &gen->gi_exc_state); | ||||
|     tstate->exc_info = gen->gi_exc_state.previous_item; | ||||
|     gen->gi_exc_state.previous_item = NULL; | ||||
|     tstate->c_recursion_remaining--; | ||||
|     assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); | ||||
|     _PyFrame_ClearExceptCode(frame); | ||||
|     _PyErr_ClearExcState(&gen->gi_exc_state); | ||||
|     tstate->c_recursion_remaining++; | ||||
|     frame->previous = NULL; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Shannon
						Mark Shannon