Issue #8020: Avoid a crash where the small objects allocator would read
non-Python managed memory while it is being modified by another thread.
Patch by Matt Bandy.
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index bbdc946..3916262 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -682,11 +682,19 @@
 obmalloc in a small constant time, independent of the number of arenas
 obmalloc controls.  Since this test is needed at every entry point, it's
 extremely desirable that it be this fast.
+
+Since Py_ADDRESS_IN_RANGE may be reading from memory which was not allocated
+by Python, it is important that (POOL)->arenaindex is read only once, as
+another thread may be concurrently modifying the value without holding the
+GIL.  To accomplish this, the arenaindex_temp variable is used to store
+(POOL)->arenaindex for the duration of the Py_ADDRESS_IN_RANGE macro's
+execution.  The caller of the macro is responsible for declaring this
+variable.
 */
 #define Py_ADDRESS_IN_RANGE(P, POOL)                    \
-    ((POOL)->arenaindex < maxarenas &&                  \
-     (uptr)(P) - arenas[(POOL)->arenaindex].address < (uptr)ARENA_SIZE && \
-     arenas[(POOL)->arenaindex].address != 0)
+    ((arenaindex_temp = (POOL)->arenaindex) < maxarenas &&              \
+     (uptr)(P) - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE && \
+     arenas[arenaindex_temp].address != 0)
 
 
 /* This is only useful when running memory debuggers such as
@@ -945,6 +953,9 @@
     block *lastfree;
     poolp next, prev;
     uint size;
+#ifndef Py_USING_MEMORY_DEBUGGER
+    uint arenaindex_temp;
+#endif
 
     if (p == NULL)      /* free(NULL) has no effect */
         return;
@@ -1167,6 +1178,9 @@
     void *bp;
     poolp pool;
     size_t size;
+#ifndef Py_USING_MEMORY_DEBUGGER
+    uint arenaindex_temp;
+#endif
 
     if (p == NULL)
         return PyObject_Malloc(nbytes);
@@ -1867,8 +1881,10 @@
 int
 Py_ADDRESS_IN_RANGE(void *P, poolp pool)
 {
-    return pool->arenaindex < maxarenas &&
-           (uptr)P - arenas[pool->arenaindex].address < (uptr)ARENA_SIZE &&
-           arenas[pool->arenaindex].address != 0;
+    uint arenaindex_temp = pool->arenaindex;
+
+    return arenaindex_temp < maxarenas &&
+           (uptr)P - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE &&
+           arenas[arenaindex_temp].address != 0;
 }
 #endif