Skip to content

Commit acc89db

Browse files
authored
bpo-45691: Make array of small ints static to fix use-after-free error. (GH-29366)
1 parent 5a14929 commit acc89db

File tree

4 files changed

+31
-45
lines changed

4 files changed

+31
-45
lines changed

Include/internal/pycore_interp.h

-14
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,6 @@ struct type_cache {
247247

248248
/* interpreter state */
249249

250-
#define _PY_NSMALLPOSINTS 257
251-
#define _PY_NSMALLNEGINTS 5
252-
253-
// _PyLong_GetZero() and _PyLong_GetOne() must always be available
254-
#if _PY_NSMALLPOSINTS < 2
255-
# error "_PY_NSMALLPOSINTS must be greater than 1"
256-
#endif
257-
258250
// The PyInterpreterState typedef is in Include/pystate.h.
259251
struct _is {
260252

@@ -330,12 +322,6 @@ struct _is {
330322

331323
PyObject *audit_hooks;
332324

333-
/* Small integers are preallocated in this array so that they
334-
can be shared.
335-
The integers that are preallocated are those in the range
336-
-_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive).
337-
*/
338-
PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
339325
struct _Py_bytes_state bytes;
340326
struct _Py_unicode_state unicode;
341327
struct _Py_float_state float_state;

Include/internal/pycore_long.h

+2-15
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,15 @@ extern "C" {
1111
#include "pycore_interp.h" // PyInterpreterState.small_ints
1212
#include "pycore_pystate.h" // _PyThreadState_GET()
1313

14-
// Don't call this function but _PyLong_GetZero() and _PyLong_GetOne()
15-
static inline PyObject* __PyLong_GetSmallInt_internal(int value)
16-
{
17-
PyInterpreterState *interp = _PyInterpreterState_GET();
18-
assert(-_PY_NSMALLNEGINTS <= value && value < _PY_NSMALLPOSINTS);
19-
size_t index = _PY_NSMALLNEGINTS + value;
20-
PyObject *obj = (PyObject*)&interp->small_ints[index];
21-
// _PyLong_GetZero(), _PyLong_GetOne() and get_small_int() must not be
22-
// called before _PyLong_Init() nor after _PyLong_Fini().
23-
assert(obj != NULL);
24-
return obj;
25-
}
26-
2714
// Return a borrowed reference to the zero singleton.
2815
// The function cannot return NULL.
2916
static inline PyObject* _PyLong_GetZero(void)
30-
{ return __PyLong_GetSmallInt_internal(0); }
17+
{ return (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS]; }
3118

3219
// Return a borrowed reference to the one singleton.
3320
// The function cannot return NULL.
3421
static inline PyObject* _PyLong_GetOne(void)
35-
{ return __PyLong_GetSmallInt_internal(1); }
22+
{ return (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS+1]; }
3623

3724
PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
3825
PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right);

Include/internal/pycore_runtime.h

+15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ extern "C" {
1111
#include "pycore_atomic.h" /* _Py_atomic_address */
1212
#include "pycore_gil.h" // struct _gil_runtime_state
1313

14+
#define _PY_NSMALLPOSINTS 257
15+
#define _PY_NSMALLNEGINTS 5
16+
17+
// _PyLong_GetZero() and _PyLong_GetOne() must always be available
18+
#if _PY_NSMALLPOSINTS < 2
19+
# error "_PY_NSMALLPOSINTS must be greater than 1"
20+
#endif
21+
1422
/* ceval state */
1523

1624
struct _ceval_runtime_state {
@@ -100,6 +108,13 @@ typedef struct pyruntimestate {
100108

101109
unsigned long main_thread;
102110

111+
/* Small integers are preallocated in this array so that they
112+
* can be shared.
113+
* The integers that are preallocated are those in the range
114+
*-_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive).
115+
*/
116+
PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
117+
103118
#define NEXITFUNCS 32
104119
void (*exitfuncs[NEXITFUNCS])(void);
105120
int nexitfuncs;

Objects/longobject.c

+14-16
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
#include "Python.h"
66
#include "pycore_bitutils.h" // _Py_popcount32()
7-
#include "pycore_interp.h" // _PY_NSMALLPOSINTS
8-
#include "pycore_long.h" // __PyLong_GetSmallInt_internal()
7+
#include "pycore_runtime.h" // _PY_NSMALLPOSINTS
8+
#include "pycore_long.h" // _Py_SmallInts
99
#include "pycore_object.h" // _PyObject_InitVar()
1010
#include "pycore_pystate.h" // _Py_IsMainInterpreter()
1111

@@ -20,9 +20,6 @@ class int "PyObject *" "&PyLong_Type"
2020
[clinic start generated code]*/
2121
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ec0275e3422a36e3]*/
2222

23-
#define NSMALLNEGINTS _PY_NSMALLNEGINTS
24-
#define NSMALLPOSINTS _PY_NSMALLPOSINTS
25-
2623
_Py_IDENTIFIER(little);
2724
_Py_IDENTIFIER(big);
2825

@@ -37,8 +34,8 @@ medium_value(PyLongObject *x)
3734
return ((stwodigits)Py_SIZE(x)) * x->ob_digit[0];
3835
}
3936

40-
#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
41-
#define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS)
37+
#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS)
38+
#define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS)
4239

4340
static inline int is_medium_int(stwodigits x)
4441
{
@@ -51,7 +48,7 @@ static PyObject *
5148
get_small_int(sdigit ival)
5249
{
5350
assert(IS_SMALL_INT(ival));
54-
PyObject *v = __PyLong_GetSmallInt_internal(ival);
51+
PyObject *v = (PyObject *)&_PyRuntime.small_ints[_PY_NSMALLNEGINTS + ival];
5552
Py_INCREF(v);
5653
return v;
5754
}
@@ -5830,17 +5827,18 @@ PyLong_GetInfo(void)
58305827
void
58315828
_PyLong_Init(PyInterpreterState *interp)
58325829
{
5833-
for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) {
5834-
sdigit ival = (sdigit)i - NSMALLNEGINTS;
5835-
int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1);
5836-
interp->small_ints[i].ob_base.ob_base.ob_refcnt = 1;
5837-
interp->small_ints[i].ob_base.ob_base.ob_type = &PyLong_Type;
5838-
interp->small_ints[i].ob_base.ob_size = size;
5839-
interp->small_ints[i].ob_digit[0] = (digit)abs(ival);
5830+
if (_PyRuntime.small_ints[0].ob_base.ob_base.ob_refcnt == 0) {
5831+
for (Py_ssize_t i=0; i < _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS; i++) {
5832+
sdigit ival = (sdigit)i - _PY_NSMALLNEGINTS;
5833+
int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1);
5834+
_PyRuntime.small_ints[i].ob_base.ob_base.ob_refcnt = 1;
5835+
_PyRuntime.small_ints[i].ob_base.ob_base.ob_type = &PyLong_Type;
5836+
_PyRuntime.small_ints[i].ob_base.ob_size = size;
5837+
_PyRuntime.small_ints[i].ob_digit[0] = (digit)abs(ival);
5838+
}
58405839
}
58415840
}
58425841

5843-
58445842
int
58455843
_PyLong_InitTypes(void)
58465844
{

0 commit comments

Comments
 (0)