Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w6s8T-004jkI-1X for pgsql-bugs@arkaria.postgresql.org; Sun, 29 Mar 2026 15:33:09 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w6s8Q-00GbHW-0R for pgsql-bugs@arkaria.postgresql.org; Sun, 29 Mar 2026 15:33:06 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w6s8P-00GbHO-2p for pgsql-bugs@lists.postgresql.org; Sun, 29 Mar 2026 15:33:06 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1w6s8M-00000001qDa-1jLd for pgsql-bugs@lists.postgresql.org; Sun, 29 Mar 2026 15:33:04 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 62TFWsTZ1830346; Sun, 29 Mar 2026 11:32:54 -0400 From: Tom Lane To: David Rowley cc: kuzmin.db4@gmail.com, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #19438: segfault with temp_file_limit inside cursor In-reply-to: References: <19438-9d37b179c56d43aa@postgresql.org> <1106026.1774573371@sss.pgh.pa.us> <1338824.1774633289@sss.pgh.pa.us> Comments: In-reply-to David Rowley message dated "Sun, 29 Mar 2026 21:43:16 +1300" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <1830293.1774798326.0@sss.pgh.pa.us> Date: Sun, 29 Mar 2026 11:32:54 -0400 Message-ID: <1830345.1774798374@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <1830293.1774798326.1@sss.pgh.pa.us> David Rowley writes: > For the switching MemoryContextMethodID patch, I applied the memory > context benchmarking patch I used when writing that code to test out > the overhead in a tight palloc/pfree loop (attached). I can see an > overhead of a little over 6.5%. Hm. I got an overhead of about 2% on an Apple M4, which might be argued to be acceptable, but 12% on an aging x86_64 platform. Realistically, given that we failed to notice this omission at all for more than three years, it's hard to argue that testing for it in non-debug builds is worth any overhead. Here's a fleshed-out version of the requested_size method. I noted that AllocSetRealloc needs a defense too, and then extended the patch to generation.c and slab.c. bump.c doesn't have an issue, and I don't think alignedalloc.c needs its own defense either: it can rely on the underlying context type. regards, tom lane ------- =_aaaaaaaaaa0 Content-Type: text/x-diff; name="v2-detect-double-free-with-requested_size.patch"; charset="us-ascii" Content-ID: <1830293.1774798326.2@sss.pgh.pa.us> Content-Description: v2-detect-double-free-with-requested_size.patch Content-Transfer-Encoding: quoted-printable diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 161c2e2d3df..bea9e47ee4f 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -1175,6 +1175,10 @@ AllocSetFree(void *pointer) link =3D GetFreeListLink(chunk); = #ifdef MEMORY_CONTEXT_CHECKING + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size =3D=3D InvalidAllocSize)) + elog(WARNING, "detected double pfree in %s %p", + set->header.name, chunk); /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx)) if (!sentinel_ok(pointer, chunk->requested_size)) @@ -1373,6 +1377,10 @@ AllocSetRealloc(void *pointer, Size size, int flags= ) oldchksize =3D GetChunkSizeFromFreeListIdx(fidx); = #ifdef MEMORY_CONTEXT_CHECKING + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size =3D=3D InvalidAllocSize)) + elog(WARNING, "detected realloc of freed chunk in %s %p", + set->header.name, chunk); /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < oldchksize) if (!sentinel_ok(pointer, chunk->requested_size)) diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/= generation.c index 9077ed299b4..fe9d087c85e 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -762,6 +762,10 @@ GenerationFree(void *pointer) } = #ifdef MEMORY_CONTEXT_CHECKING + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size =3D=3D InvalidAllocSize)) + elog(WARNING, "detected double pfree in %s %p", + ((MemoryContext) block->context)->name, chunk); /* Test for someone scribbling on unused space in chunk */ Assert(chunk->requested_size < chunksize); if (!sentinel_ok(pointer, chunk->requested_size)) @@ -867,6 +871,10 @@ GenerationRealloc(void *pointer, Size size, int flags= ) set =3D block->context; = #ifdef MEMORY_CONTEXT_CHECKING + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size =3D=3D InvalidAllocSize)) + elog(WARNING, "detected realloc of freed chunk in %s %p", + ((MemoryContext) set)->name, chunk); /* Test for someone scribbling on unused space in chunk */ Assert(chunk->requested_size < oldsize); if (!sentinel_ok(pointer, chunk->requested_size)) diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index bd00bab18fe..0d1e6285c29 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -539,6 +539,7 @@ SlabAllocSetupNewChunk(MemoryContext context, SlabBloc= k *block, MemoryChunkSetHdrMask(chunk, block, MAXALIGN(slab->chunkSize), MCTX_SLAB= _ID); = #ifdef MEMORY_CONTEXT_CHECKING + chunk->requested_size =3D size; /* slab mark to catch clobber of "unused" space */ Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ)); set_sentinel(MemoryChunkGetPointer(chunk), size); @@ -748,11 +749,17 @@ SlabFree(void *pointer) slab =3D block->slab; = #ifdef MEMORY_CONTEXT_CHECKING + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size =3D=3D InvalidAllocSize)) + elog(WARNING, "detected double pfree in %s %p", + slab->header.name, chunk); /* Test for someone scribbling on unused space in chunk */ Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ)); if (!sentinel_ok(pointer, slab->chunkSize)) elog(WARNING, "detected write past chunk end in %s %p", slab->header.name, chunk); + /* Reset requested_size to InvalidAllocSize in free chunks */ + chunk->requested_size =3D InvalidAllocSize; #endif = /* push this chunk onto the head of the block's free list */ ------- =_aaaaaaaaaa0--