171171
172172/*
173173 * Size of the pools used for small blocks. Should be a power of 2,
174- * between 1K and SYSTEM_PAGE_SIZE, that is: 1k, 2k, 4k, eventually 8k .
174+ * between 1K and SYSTEM_PAGE_SIZE, that is: 1k, 2k, 4k.
175175 */
176176#define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */
177177#define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK
@@ -308,8 +308,8 @@ static poolp freepools = NULL; /* free list for cached pools */
308308 * and only grows by appending at the end. For now we never return an arena
309309 * to the OS.
310310 */
311- static uptr * arenas = NULL ;
312- static uint narenas = 0 ;
311+ static uptr * volatile arenas = NULL ; /* the pointer itself is volatile */
312+ static volatile uint narenas = 0 ;
313313static uint maxarenas = 0 ;
314314
315315/* Number of pools still available to be allocated in the current arena. */
@@ -354,6 +354,7 @@ new_arena(void)
354354 nfreepools <- number of whole pools that fit after alignment */
355355 arenabase = bp ;
356356 nfreepools = ARENA_SIZE / POOL_SIZE ;
357+ assert (POOL_SIZE * nfreepools == ARENA_SIZE );
357358 excess = (uint )bp & POOL_SIZE_MASK ;
358359 if (excess != 0 ) {
359360 -- nfreepools ;
@@ -362,15 +363,16 @@ new_arena(void)
362363
363364 /* Make room for a new entry in the arenas vector. */
364365 if (arenas == NULL ) {
366+ assert (narenas == 0 && maxarenas == 0 );
365367 arenas = (uptr * )PyMem_MALLOC (16 * sizeof (* arenas ));
366368 if (arenas == NULL )
367369 goto error ;
368370 maxarenas = 16 ;
369- narenas = 0 ;
370371 }
371372 else if (narenas == maxarenas ) {
372373 /* Grow arenas. Don't use realloc: if this fails, we
373374 * don't want to lose the base addresses we already have.
375+ *
374376 * Exceedingly subtle: Someone may be calling the pymalloc
375377 * free via PyMem_{DEL, Del, FREE, Free} without holding the
376378 *.GIL. Someone else may simultaneously be calling the
@@ -392,26 +394,30 @@ new_arena(void)
392394 * is supposed to fail. Having an incomplete vector can't
393395 * make a supposed-to-fail case succeed by mistake (it could
394396 * only make a supposed-to-succeed case fail by mistake).
397+ *
398+ * In addition, without a lock we can't know for sure when
399+ * an old vector is no longer referenced, so we simply let
400+ * old vectors leak.
401+ *
402+ * And on top of that, since narenas and arenas can't be
403+ * changed as-a-pair atomically without a lock, we're also
404+ * careful to declare them volatile and ensure that we change
405+ * arenas first. This prevents another thread from picking
406+ * up an narenas value too large for the arenas value it
407+ * reads up (arenas never shrinks).
408+ *
395409 * Read the above 50 times before changing anything in this
396410 * block.
397- * XXX Fudge. This is still vulnerable: there's nothing
398- * XXX to stop the bad-guy thread from picking up the
399- * XXX current value of arenas, but not indexing off of it
400- * XXX until after the PyMem_FREE(oldarenas) below completes.
401411 */
402- uptr * oldarenas ;
403412 uptr * p ;
404- uint newmax = maxarenas + (maxarenas >> 1 );
405-
413+ uint newmax = maxarenas << 1 ;
406414 if (newmax <= maxarenas ) /* overflow */
407415 goto error ;
408416 p = (uptr * )PyMem_MALLOC (newmax * sizeof (* arenas ));
409417 if (p == NULL )
410418 goto error ;
411419 memcpy (p , arenas , narenas * sizeof (* arenas ));
412- oldarenas = arenas ;
413- arenas = p ;
414- PyMem_FREE (oldarenas );
420+ arenas = p ; /* old arenas deliberately leaked */
415421 maxarenas = newmax ;
416422 }
417423
@@ -431,12 +437,19 @@ new_arena(void)
431437/* Return true if and only if P is an address that was allocated by
432438 * pymalloc. I must be the index into arenas that the address claims
433439 * to come from.
440+ *
434441 * Tricky: Letting B be the arena base address in arenas[I], P belongs to the
435442 * arena if and only if
436443 * B <= P < B + ARENA_SIZE
437444 * Subtracting B throughout, this is true iff
438445 * 0 <= P-B < ARENA_SIZE
439446 * By using unsigned arithmetic, the "0 <=" half of the test can be skipped.
447+ *
448+ * Obscure: A PyMem "free memory" function can call the pymalloc free or
449+ * realloc before the first arena has been allocated. arenas is still
450+ * NULL in that case. We're relying on that narenas is also 0 in that case,
451+ * so the (I) < narenas must be false, saving us from trying to index into
452+ * a NULL arenas.
440453 */
441454#define ADDRESS_IN_RANGE (P , I ) \
442455 ((I) < narenas && (uptr)(P) - arenas[I] < (uptr)ARENA_SIZE)
0 commit comments