[Python-checkins] bpo-45691: Make array of small ints static to fix use-after-free error. (GH-29366)

markshannon webhook-mailer at python.org
Wed Nov 3 12:22:42 EDT 2021


https://github.com/python/cpython/commit/acc89db9233abf4d903af9a7595a2ed7478fe7d3
commit: acc89db9233abf4d903af9a7595a2ed7478fe7d3
branch: main
author: Mark Shannon <mark at hotpy.org>
committer: markshannon <mark at hotpy.org>
date: 2021-11-03T16:22:32Z
summary:

bpo-45691: Make array of small ints static to fix use-after-free error. (GH-29366)

files:
M Include/internal/pycore_interp.h
M Include/internal/pycore_long.h
M Include/internal/pycore_runtime.h
M Objects/longobject.c

diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 8bd3dc064eea3..18f21432008d9 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 773025b4a5add..2f786083e4527 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 bcd710c4496bd..9df833c270193 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 b7392e50e7f6c..a4d90b1704321 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)
 {



More information about the Python-checkins mailing list