From acc89db9233abf4d903af9a7595a2ed7478fe7d3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Nov 2021 16:22:32 +0000 Subject: [PATCH] bpo-45691: Make array of small ints static to fix use-after-free error. (GH-29366) --- Include/internal/pycore_interp.h | 14 -------------- Include/internal/pycore_long.h | 17 ++--------------- Include/internal/pycore_runtime.h | 15 +++++++++++++++ Objects/longobject.c | 30 ++++++++++++++---------------- 4 files changed, 31 insertions(+), 45 deletions(-) diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 8bd3dc064ee..18f21432008 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -247,14 +247,6 @@ struct type_cache { /* interpreter state */ -#define _PY_NSMALLPOSINTS 257 -#define _PY_NSMALLNEGINTS 5 - -// _PyLong_GetZero() and _PyLong_GetOne() must always be available -#if _PY_NSMALLPOSINTS < 2 -# error "_PY_NSMALLPOSINTS must be greater than 1" -#endif - // The PyInterpreterState typedef is in Include/pystate.h. struct _is { @@ -330,12 +322,6 @@ struct _is { PyObject *audit_hooks; - /* Small integers are preallocated in this array so that they - can be shared. - The integers that are preallocated are those in the range - -_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive). - */ - PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; struct _Py_bytes_state bytes; struct _Py_unicode_state unicode; struct _Py_float_state float_state; diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 773025b4a5a..2f786083e45 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -11,28 +11,15 @@ extern "C" { #include "pycore_interp.h" // PyInterpreterState.small_ints #include "pycore_pystate.h" // _PyThreadState_GET() -// Don't call this function but _PyLong_GetZero() and _PyLong_GetOne() -static inline PyObject* __PyLong_GetSmallInt_internal(int value) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - assert(-_PY_NSMALLNEGINTS <= value && value < _PY_NSMALLPOSINTS); - size_t index = _PY_NSMALLNEGINTS + value; - PyObject *obj = (PyObject*)&interp->small_ints[index]; - // _PyLong_GetZero(), _PyLong_GetOne() and get_small_int() must not be - // called before _PyLong_Init() nor after _PyLong_Fini(). - assert(obj != NULL); - return obj; -} - // Return a borrowed reference to the zero singleton. // The function cannot return NULL. static inline PyObject* _PyLong_GetZero(void) -{ return __PyLong_GetSmallInt_internal(0); } +{ return (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS]; } // Return a borrowed reference to the one singleton. // The function cannot return NULL. static inline PyObject* _PyLong_GetOne(void) -{ return __PyLong_GetSmallInt_internal(1); } +{ return (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS+1]; } PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right); PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right); diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index bcd710c4496..9df833c2701 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -11,6 +11,14 @@ extern "C" { #include "pycore_atomic.h" /* _Py_atomic_address */ #include "pycore_gil.h" // struct _gil_runtime_state +#define _PY_NSMALLPOSINTS 257 +#define _PY_NSMALLNEGINTS 5 + +// _PyLong_GetZero() and _PyLong_GetOne() must always be available +#if _PY_NSMALLPOSINTS < 2 +# error "_PY_NSMALLPOSINTS must be greater than 1" +#endif + /* ceval state */ struct _ceval_runtime_state { @@ -100,6 +108,13 @@ typedef struct pyruntimestate { unsigned long main_thread; + /* Small integers are preallocated in this array so that they + * can be shared. + * The integers that are preallocated are those in the range + *-_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive). + */ + PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; + #define NEXITFUNCS 32 void (*exitfuncs[NEXITFUNCS])(void); int nexitfuncs; diff --git a/Objects/longobject.c b/Objects/longobject.c index b7392e50e7f..a4d90b17043 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4,8 +4,8 @@ #include "Python.h" #include "pycore_bitutils.h" // _Py_popcount32() -#include "pycore_interp.h" // _PY_NSMALLPOSINTS -#include "pycore_long.h" // __PyLong_GetSmallInt_internal() +#include "pycore_runtime.h" // _PY_NSMALLPOSINTS +#include "pycore_long.h" // _Py_SmallInts #include "pycore_object.h" // _PyObject_InitVar() #include "pycore_pystate.h" // _Py_IsMainInterpreter() @@ -20,9 +20,6 @@ class int "PyObject *" "&PyLong_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=ec0275e3422a36e3]*/ -#define NSMALLNEGINTS _PY_NSMALLNEGINTS -#define NSMALLPOSINTS _PY_NSMALLPOSINTS - _Py_IDENTIFIER(little); _Py_IDENTIFIER(big); @@ -37,8 +34,8 @@ medium_value(PyLongObject *x) return ((stwodigits)Py_SIZE(x)) * x->ob_digit[0]; } -#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS) -#define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS) +#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) +#define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS) static inline int is_medium_int(stwodigits x) { @@ -51,7 +48,7 @@ static PyObject * get_small_int(sdigit ival) { assert(IS_SMALL_INT(ival)); - PyObject *v = __PyLong_GetSmallInt_internal(ival); + PyObject *v = (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS + ival]; Py_INCREF(v); return v; } @@ -5830,17 +5827,18 @@ PyLong_GetInfo(void) void _PyLong_Init(PyInterpreterState *interp) { - for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { - sdigit ival = (sdigit)i - NSMALLNEGINTS; - int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1); - interp->small_ints[i].ob_base.ob_base.ob_refcnt = 1; - interp->small_ints[i].ob_base.ob_base.ob_type = &PyLong_Type; - interp->small_ints[i].ob_base.ob_size = size; - interp->small_ints[i].ob_digit[0] = (digit)abs(ival); + if (_PyRuntime.small_ints[0].ob_base.ob_base.ob_refcnt == 0) { + for (Py_ssize_t i=0; i < _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS; i++) { + sdigit ival = (sdigit)i - _PY_NSMALLNEGINTS; + int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1); + _PyRuntime.small_ints[i].ob_base.ob_base.ob_refcnt = 1; + _PyRuntime.small_ints[i].ob_base.ob_base.ob_type = &PyLong_Type; + _PyRuntime.small_ints[i].ob_base.ob_size = size; + _PyRuntime.small_ints[i].ob_digit[0] = (digit)abs(ival); + } } } - int _PyLong_InitTypes(void) {