From cf816d60be99ec3aaedef40a050256aba93a4488 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 2 Nov 2021 16:06:23 +0000 Subject: [PATCH 1/2] Make array of small ints static to fix use-after-free error. --- Include/internal/pycore_interp.h | 5 ----- Include/internal/pycore_long.h | 22 ++++++++-------------- Objects/longobject.c | 23 +++++++++++++---------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 8bd3dc064eea31..06aa40bd10783b 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -330,11 +330,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; diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 773025b4a5add5..a7367f579b142b 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -11,28 +11,22 @@ 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; -} +/* 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). +*/ +PyAPI_DATA(PyLongObject) _Py_SmallInts[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; // 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 *)&_Py_SmallInts[_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 *)&_Py_SmallInts[_PY_NSMALLNEGINTS+1]; } PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right); PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right); diff --git a/Objects/longobject.c b/Objects/longobject.c index b7392e50e7f6c2..15a34613a6b8b6 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5,7 +5,7 @@ #include "Python.h" #include "pycore_bitutils.h" // _Py_popcount32() #include "pycore_interp.h" // _PY_NSMALLPOSINTS -#include "pycore_long.h" // __PyLong_GetSmallInt_internal() +#include "pycore_long.h" // _Py_SmallInts #include "pycore_object.h" // _PyObject_InitVar() #include "pycore_pystate.h" // _Py_IsMainInterpreter() @@ -51,7 +51,7 @@ static PyObject * get_small_int(sdigit ival) { assert(IS_SMALL_INT(ival)); - PyObject *v = __PyLong_GetSmallInt_internal(ival); + PyObject *v = (PyObject *)&_Py_SmallInts[_PY_NSMALLNEGINTS + ival]; Py_INCREF(v); return v; } @@ -5827,20 +5827,23 @@ PyLong_GetInfo(void) return int_info; } +PyLongObject _Py_SmallInts[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS] = { 0 }; + 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 (_Py_SmallInts[0].ob_base.ob_base.ob_refcnt == 0) { + for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { + sdigit ival = (sdigit)i - NSMALLNEGINTS; + int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1); + _Py_SmallInts[i].ob_base.ob_base.ob_refcnt = 1; + _Py_SmallInts[i].ob_base.ob_base.ob_type = &PyLong_Type; + _Py_SmallInts[i].ob_base.ob_size = size; + _Py_SmallInts[i].ob_digit[0] = (digit)abs(ival); + } } } - int _PyLong_InitTypes(void) { From 9ee9157d15791e2886a5d7bbd392c6ef729a13f2 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Nov 2021 11:58:39 +0000 Subject: [PATCH 2/2] Move small ints into runtime-state. --- Include/internal/pycore_interp.h | 9 --------- Include/internal/pycore_long.h | 11 ++--------- Include/internal/pycore_runtime.h | 15 +++++++++++++++ Objects/longobject.c | 27 +++++++++++---------------- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 06aa40bd10783b..18f21432008d96 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,7 +322,6 @@ struct _is { PyObject *audit_hooks; - 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 a7367f579b142b..2f786083e45275 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -11,22 +11,15 @@ extern "C" { #include "pycore_interp.h" // PyInterpreterState.small_ints #include "pycore_pystate.h" // _PyThreadState_GET() -/* 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). -*/ -PyAPI_DATA(PyLongObject) _Py_SmallInts[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; - // Return a borrowed reference to the zero singleton. // The function cannot return NULL. static inline PyObject* _PyLong_GetZero(void) -{ return (PyObject *)&_Py_SmallInts[_PY_NSMALLNEGINTS]; } +{ 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 (PyObject *)&_Py_SmallInts[_PY_NSMALLNEGINTS+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 bcd710c4496bd7..9df833c2701934 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 15a34613a6b8b6..a4d90b17043215 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4,7 +4,7 @@ #include "Python.h" #include "pycore_bitutils.h" // _Py_popcount32() -#include "pycore_interp.h" // _PY_NSMALLPOSINTS +#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 = (PyObject *)&_Py_SmallInts[_PY_NSMALLNEGINTS + ival]; + PyObject *v = (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS + ival]; Py_INCREF(v); return v; } @@ -5827,19 +5824,17 @@ PyLong_GetInfo(void) return int_info; } -PyLongObject _Py_SmallInts[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS] = { 0 }; - void _PyLong_Init(PyInterpreterState *interp) { - if (_Py_SmallInts[0].ob_base.ob_base.ob_refcnt == 0) { - for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { - sdigit ival = (sdigit)i - NSMALLNEGINTS; + 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); - _Py_SmallInts[i].ob_base.ob_base.ob_refcnt = 1; - _Py_SmallInts[i].ob_base.ob_base.ob_type = &PyLong_Type; - _Py_SmallInts[i].ob_base.ob_size = size; - _Py_SmallInts[i].ob_digit[0] = (digit)abs(ival); + _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); } } }