1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-18 02:02:55 +03:00

Fix MemoryContextAllocAligned's interaction with Valgrind.

Arrange that only the "aligned chunk" part of the allocated space is
included in a Valgrind vchunk.  This suppresses complaints about that
vchunk being possibly lost because PG is retaining only pointers to
the aligned chunk.  Also make sure that trailing wasted space is
marked NOACCESS.

As a tiny performance improvement, arrange that MCXT_ALLOC_ZERO zeroes
only the returned "aligned chunk", not the wasted padding space.

In passing, fix GetLocalBufferStorage to use MemoryContextAllocAligned
instead of rolling its own implementation, which was equally broken
according to Valgrind.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2025-08-02 18:31:39 -04:00
parent bb049a79d3
commit 9e9190154e
3 changed files with 56 additions and 25 deletions

View File

@@ -932,10 +932,11 @@ GetLocalBufferStorage(void)
num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ); num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
/* Buffers should be I/O aligned. */ /* Buffers should be I/O aligned. */
cur_block = (char *) cur_block = MemoryContextAllocAligned(LocalBufferContext,
TYPEALIGN(PG_IO_ALIGN_SIZE, num_bufs * BLCKSZ,
MemoryContextAlloc(LocalBufferContext, PG_IO_ALIGN_SIZE,
num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE)); 0);
next_buf_in_block = 0; next_buf_in_block = 0;
num_bufs_in_block = num_bufs; num_bufs_in_block = num_bufs;
} }

View File

@@ -45,6 +45,15 @@ AlignedAllocFree(void *pointer)
GetMemoryChunkContext(unaligned)->name, chunk); GetMemoryChunkContext(unaligned)->name, chunk);
#endif #endif
/*
* Create a dummy vchunk covering the start of the unaligned chunk, but
* not overlapping the aligned chunk. This will be freed while pfree'ing
* the unaligned chunk, keeping Valgrind happy. Then when we return to
* the outer pfree, that will clean up the vchunk for the aligned chunk.
*/
VALGRIND_MEMPOOL_ALLOC(GetMemoryChunkContext(unaligned), unaligned,
(char *) pointer - (char *) unaligned);
/* Recursively pfree the unaligned chunk */ /* Recursively pfree the unaligned chunk */
pfree(unaligned); pfree(unaligned);
} }
@@ -123,6 +132,15 @@ AlignedAllocRealloc(void *pointer, Size size, int flags)
VALGRIND_MAKE_MEM_DEFINED(pointer, old_size); VALGRIND_MAKE_MEM_DEFINED(pointer, old_size);
memcpy(newptr, pointer, Min(size, old_size)); memcpy(newptr, pointer, Min(size, old_size));
/*
* Create a dummy vchunk covering the start of the old unaligned chunk,
* but not overlapping the aligned chunk. This will be freed while
* pfree'ing the old unaligned chunk, keeping Valgrind happy. Then when
* we return to repalloc, it will move the vchunk for the aligned chunk.
*/
VALGRIND_MEMPOOL_ALLOC(ctx, unaligned,
(char *) pointer - (char *) unaligned);
pfree(unaligned); pfree(unaligned);
return newptr; return newptr;

View File

@@ -1465,7 +1465,13 @@ MemoryContextAllocAligned(MemoryContext context,
void *unaligned; void *unaligned;
void *aligned; void *aligned;
/* wouldn't make much sense to waste that much space */ /*
* Restrict alignto to ensure that it can fit into the "value" field of
* the redirection MemoryChunk, and that the distance back to the start of
* the unaligned chunk will fit into the space available for that. This
* isn't a limitation in practice, since it wouldn't make much sense to
* waste that much space.
*/
Assert(alignto < (128 * 1024 * 1024)); Assert(alignto < (128 * 1024 * 1024));
/* ensure alignto is a power of 2 */ /* ensure alignto is a power of 2 */
@@ -1502,10 +1508,15 @@ MemoryContextAllocAligned(MemoryContext context,
alloc_size += 1; alloc_size += 1;
#endif #endif
/* perform the actual allocation */ /*
unaligned = MemoryContextAllocExtended(context, alloc_size, flags); * Perform the actual allocation, but do not pass down MCXT_ALLOC_ZERO.
* This ensures that wasted bytes beyond the aligned chunk do not become
* DEFINED.
*/
unaligned = MemoryContextAllocExtended(context, alloc_size,
flags & ~MCXT_ALLOC_ZERO);
/* set the aligned pointer */ /* compute the aligned pointer */
aligned = (void *) TYPEALIGN(alignto, (char *) unaligned + aligned = (void *) TYPEALIGN(alignto, (char *) unaligned +
sizeof(MemoryChunk)); sizeof(MemoryChunk));
@@ -1533,12 +1544,23 @@ MemoryContextAllocAligned(MemoryContext context,
set_sentinel(aligned, size); set_sentinel(aligned, size);
#endif #endif
/* Mark the bytes before the redirection header as noaccess */ /*
VALGRIND_MAKE_MEM_NOACCESS(unaligned, * MemoryContextAllocExtended marked the whole unaligned chunk as a
(char *) alignedchunk - (char *) unaligned); * vchunk. Undo that, instead making just the aligned chunk be a vchunk.
* This prevents Valgrind from complaining that the vchunk is possibly
* leaked, since only pointers to the aligned chunk will exist.
*
* After these calls, the aligned chunk will be marked UNDEFINED, and all
* the rest of the unaligned chunk (the redirection chunk header, the
* padding bytes before it, and any wasted trailing bytes) will be marked
* NOACCESS, which is what we want.
*/
VALGRIND_MEMPOOL_FREE(context, unaligned);
VALGRIND_MEMPOOL_ALLOC(context, aligned, size);
/* Disallow access to the redirection chunk header. */ /* Now zero (and make DEFINED) just the aligned chunk, if requested */
VALGRIND_MAKE_MEM_NOACCESS(alignedchunk, sizeof(MemoryChunk)); if ((flags & MCXT_ALLOC_ZERO) != 0)
MemSetAligned(aligned, 0, size);
return aligned; return aligned;
} }
@@ -1572,16 +1594,12 @@ void
pfree(void *pointer) pfree(void *pointer)
{ {
#ifdef USE_VALGRIND #ifdef USE_VALGRIND
MemoryContextMethodID method = GetMemoryChunkMethodID(pointer);
MemoryContext context = GetMemoryChunkContext(pointer); MemoryContext context = GetMemoryChunkContext(pointer);
#endif #endif
MCXT_METHOD(pointer, free_p) (pointer); MCXT_METHOD(pointer, free_p) (pointer);
#ifdef USE_VALGRIND VALGRIND_MEMPOOL_FREE(context, pointer);
if (method != MCTX_ALIGNED_REDIRECT_ID)
VALGRIND_MEMPOOL_FREE(context, pointer);
#endif
} }
/* /*
@@ -1591,9 +1609,6 @@ pfree(void *pointer)
void * void *
repalloc(void *pointer, Size size) repalloc(void *pointer, Size size)
{ {
#ifdef USE_VALGRIND
MemoryContextMethodID method = GetMemoryChunkMethodID(pointer);
#endif
#if defined(USE_ASSERT_CHECKING) || defined(USE_VALGRIND) #if defined(USE_ASSERT_CHECKING) || defined(USE_VALGRIND)
MemoryContext context = GetMemoryChunkContext(pointer); MemoryContext context = GetMemoryChunkContext(pointer);
#endif #endif
@@ -1616,10 +1631,7 @@ repalloc(void *pointer, Size size)
*/ */
ret = MCXT_METHOD(pointer, realloc) (pointer, size, 0); ret = MCXT_METHOD(pointer, realloc) (pointer, size, 0);
#ifdef USE_VALGRIND VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
if (method != MCTX_ALIGNED_REDIRECT_ID)
VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
#endif
return ret; return ret;
} }