40288931d6f7e083f7b71fd97109c03d9916bc30
jcasper
  Thu Aug 27 09:30:25 2026 -0700
When memalloc fails, it calls errAbort.  But if the handler is a sophisticated one, it might try
to call memalloc itself, resulting in an endless loop.  Better to nip that in the bud.  no ticket

diff --git src/lib/memalloc.c src/lib/memalloc.c
index 3ff198f1793..17d474db5c3 100644
--- src/lib/memalloc.c
+++ src/lib/memalloc.c
@@ -1,30 +1,31 @@
 /* memalloc.c - Routines to allocate and deallocate dynamic memory. 
  * This lets you have a stack of memory handlers.  The default
  * memory handler is a thin shell around malloc/free.  You can
  * substitute routines that do more integrety checking with
  * pushCarefulMem(), or routines of your own devising with
  * pushMemHandler(). 
  *
  * This file is copyright 2002 Jim Kent, but license is hereby
  * granted for all use - public, private or commercial. */
 
 #include <pthread.h>
 #include "common.h"
 #include "obscure.h"
 #include "memalloc.h"
 #include "dlist.h"
+#include "errAbort.h"
 
 
 static size_t memAlloced = 0;
 
 size_t memCheckPoint()
 /* Return the amount of memory allocated since last called. */
 {
 size_t ret = memAlloced;
 
 memAlloced = 0;
 
 return ret;
 }
 
 static void *defaultAlloc(size_t size)
@@ -192,32 +193,42 @@
 #define NEEDMEM_LIMIT 500000000
 
 void *needMem(size_t size)
 /* Need mem calls abort if the memory allocation fails. The memory
  * is initialized to zero. */
 {
 void *pt;
 
 if (size >= 0x8000000000000000)  // caused by overflowed signed int getting sign-extended
     size += 4294967296;
 
 if (size == 0 || size > NEEDMEM_LIMIT)
     errAbort("needMem: trying to allocate %llu bytes (limit: %llu)",
          (unsigned long long)size, (unsigned long long)NEEDMEM_LIMIT);
 if ((pt = mhStack->alloc(size)) == NULL)
+    {
+    if (isErrAbortInProgress())
+        {
+        /* Calling errAbort here would re-enter the warn handler and loop.
+         * dumpStack makes no heap allocations (fork+pstack) and has its own
+         * re-entrancy guard; exit immediately after. */
+        dumpStack("needMem: out of memory during error handling");
+        exit(1);
+        }
     errAbort("needMem: Out of memory - request size %llu bytes, errno: %d\n",
              (unsigned long long)size, errno);
+    }
 memset(pt, 0, size);
 memAlloced += size;
 return pt;
 }
 
 void *needMoreMem(void *old, size_t oldSize, size_t newSize)
 /* Adjust memory size on a block, possibly relocating it.  If vp is NULL, a
  * new memory block is allocated.  No checking on size.  If block is grown,
  * new memory is zeroed. */
 {
 return needLargeZeroedMemResize(old, oldSize, newSize);
 }
 
 void *wantMem(size_t size)
 /* Want mem just calls malloc - no zeroing of memory, no