diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index c86f83e1dac..707c3b77f12 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -1125,7 +1125,26 @@ AllocSetFree(void *pointer) Assert(FreeListIdxIsValid(fidx)); link = GetFreeListLink(chunk); + /* + * It might seem odd that we use elevel ERROR for double-pfree but + * only WARNING for write-past-chunk-end. But the two conditions are + * not very comparable. In the double-pfree case we can prevent + * corruption before it happens; while if we let it go through, the + * result would be a corrupted freelist that allows this chunk to get + * re-allocated twice. Thus the original bug could cascade into + * hard-to-understand misbehavior that might manifest far away from + * the actual source of the problem. On the other hand, a write past + * chunk end can be relatively benign if just a few bytes too many + * were written: often, only padding or unused space gets affected. + * Moreover, whatever damage was done is already done, and we're just + * reporting after the fact with no ability to clean it up. So just + * warn, like AllocSetCheck would do if the chunk didn't get freed. + */ #ifdef MEMORY_CONTEXT_CHECKING + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size == InvalidAllocSize)) + elog(ERROR, "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)) @@ -1316,6 +1335,11 @@ AllocSetRealloc(void *pointer, Size size, int flags) oldchksize = GetChunkSizeFromFreeListIdx(fidx); #ifdef MEMORY_CONTEXT_CHECKING + /* See comments in AllocSetFree about uses of ERROR and WARNING here */ + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size == InvalidAllocSize)) + elog(ERROR, "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 0238c111d2f..f19890d0e53 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -733,6 +733,11 @@ GenerationFree(void *pointer) } #ifdef MEMORY_CONTEXT_CHECKING + /* See comments in AllocSetFree about uses of ERROR and WARNING here */ + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size == InvalidAllocSize)) + elog(ERROR, "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)) @@ -838,6 +843,11 @@ GenerationRealloc(void *pointer, Size size, int flags) set = block->context; #ifdef MEMORY_CONTEXT_CHECKING + /* See comments in AllocSetFree about uses of ERROR and WARNING here */ + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size == InvalidAllocSize)) + elog(ERROR, "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 3e15d59683f..0f0148746b8 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -514,6 +514,7 @@ SlabAllocSetupNewChunk(MemoryContext context, SlabBlock *block, MemoryChunkSetHdrMask(chunk, block, MAXALIGN(slab->chunkSize), MCTX_SLAB_ID); #ifdef MEMORY_CONTEXT_CHECKING + chunk->requested_size = size; /* slab mark to catch clobber of "unused" space */ Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ)); set_sentinel(MemoryChunkGetPointer(chunk), size); @@ -720,11 +721,18 @@ SlabFree(void *pointer) slab = block->slab; #ifdef MEMORY_CONTEXT_CHECKING + /* See comments in AllocSetFree about uses of ERROR and WARNING here */ + /* Test for previously-freed chunk */ + if (unlikely(chunk->requested_size == InvalidAllocSize)) + elog(ERROR, "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 = InvalidAllocSize; #endif /* push this chunk onto the head of the block's free list */