Skip to content

Commit b3e184a

Browse files
committed
Fix erroneous Valgrind markings in AllocSetRealloc.
If asked to decrease the size of a large (>8K) palloc chunk, AllocSetRealloc could improperly change the Valgrind state of memory beyond the new end of the chunk: it would mark data UNDEFINED as far as the old end of the chunk after having done the realloc(3) call, thus tromping on the state of memory that no longer belongs to it. One would normally expect that memory to now be marked NOACCESS, so that this mislabeling might prevent detection of later errors. If realloc() had chosen to move the chunk someplace else (unlikely, but well within its rights) we could also mismark perfectly-valid DEFINED data as UNDEFINED, causing false-positive valgrind reports later. Also, any malloc bookkeeping data placed within this area might now be wrongly marked, causing additional problems. Fix by replacing relevant uses of "oldsize" with "Min(size, oldsize)". It's sufficient to mark as far as "size" when that's smaller, because whatever remains in the new chunk size will be marked NOACCESS below, and we expect realloc() to have taken care of marking the memory beyond the new official end of the chunk. While we're here, also rename the function's "oldsize" variable to "oldchksize" to more clearly explain what it actually holds, namely the distance to the end of the chunk (that is, requested size plus trailing padding). This is more consistent with the use of "size" and "chksize" to hold the new requested size and chunk size. Add a new variable "oldsize" in the one stanza where we're actually talking about the old requested size. Oversight in commit c477f3e. Back-patch to all supported branches, as that was, just in case anybody wants to do valgrind testing on back branches. Karina Litskevich Discussion: https://postgr.es/m/CACiT8iaAET-fmzjjZLjaJC4zwSJmrFyL7LAdHwaYyjjQOQ4hcg@mail.gmail.com
1 parent a1f45f6 commit b3e184a

File tree

1 file changed

+33
-20
lines changed

1 file changed

+33
-20
lines changed

src/backend/utils/mmgr/aset.c

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ AllocSetRealloc(void *pointer, Size size)
11121112
AllocBlock block;
11131113
AllocSet set;
11141114
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1115-
Size oldsize;
1115+
Size oldchksize;
11161116
int fidx;
11171117

11181118
/* Allow access to private part of chunk header. */
@@ -1140,11 +1140,11 @@ AllocSetRealloc(void *pointer, Size size)
11401140

11411141
set = block->aset;
11421142

1143-
oldsize = block->endptr - (char *) pointer;
1143+
oldchksize = block->endptr - (char *) pointer;
11441144

11451145
#ifdef MEMORY_CONTEXT_CHECKING
11461146
/* Test for someone scribbling on unused space in chunk */
1147-
Assert(chunk->requested_size < oldsize);
1147+
Assert(chunk->requested_size < oldchksize);
11481148
if (!sentinel_ok(pointer, chunk->requested_size))
11491149
elog(WARNING, "detected write past chunk end in %s %p",
11501150
set->header.name, chunk);
@@ -1187,21 +1187,29 @@ AllocSetRealloc(void *pointer, Size size)
11871187

11881188
#ifdef MEMORY_CONTEXT_CHECKING
11891189
#ifdef RANDOMIZE_ALLOCATED_MEMORY
1190-
/* We can only fill the extra space if we know the prior request */
1190+
1191+
/*
1192+
* We can only randomize the extra space if we know the prior request.
1193+
* When using Valgrind, randomize_mem() also marks memory UNDEFINED.
1194+
*/
11911195
if (size > chunk->requested_size)
11921196
randomize_mem((char *) pointer + chunk->requested_size,
11931197
size - chunk->requested_size);
1194-
#endif
1198+
#else
11951199

11961200
/*
1197-
* realloc() (or randomize_mem()) will have left any newly-allocated
1198-
* part UNDEFINED, but we may need to adjust trailing bytes from the
1199-
* old allocation.
1201+
* If this is an increase, realloc() will have marked any
1202+
* newly-allocated part (from oldchksize to chksize) UNDEFINED, but we
1203+
* also need to adjust trailing bytes from the old allocation (from
1204+
* chunk->requested_size to oldchksize) as they are marked NOACCESS.
1205+
* Make sure not to mark too many bytes in case chunk->requested_size
1206+
* < size < oldchksize.
12001207
*/
12011208
#ifdef USE_VALGRIND
1202-
if (oldsize > chunk->requested_size)
1209+
if (Min(size, oldchksize) > chunk->requested_size)
12031210
VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + chunk->requested_size,
1204-
oldsize - chunk->requested_size);
1211+
Min(size, oldchksize) - chunk->requested_size);
1212+
#endif
12051213
#endif
12061214

12071215
chunk->requested_size = size;
@@ -1211,11 +1219,14 @@ AllocSetRealloc(void *pointer, Size size)
12111219
#else /* !MEMORY_CONTEXT_CHECKING */
12121220

12131221
/*
1214-
* We don't know how much of the old chunk size was the actual
1215-
* allocation; it could have been as small as one byte. We have to be
1216-
* conservative and just mark the entire old portion DEFINED.
1222+
* We may need to adjust marking of bytes from the old allocation as
1223+
* some of them may be marked NOACCESS. We don't know how much of the
1224+
* old chunk size was the requested size; it could have been as small
1225+
* as one byte. We have to be conservative and just mark the entire
1226+
* old portion DEFINED. Make sure not to mark memory beyond the new
1227+
* allocation in case it's smaller than the old one.
12171228
*/
1218-
VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
1229+
VALGRIND_MAKE_MEM_DEFINED(pointer, Min(size, oldchksize));
12191230
#endif
12201231

12211232
/* Ensure any padding bytes are marked NOACCESS. */
@@ -1240,11 +1251,11 @@ AllocSetRealloc(void *pointer, Size size)
12401251

12411252
fidx = MemoryChunkGetValue(chunk);
12421253
Assert(FreeListIdxIsValid(fidx));
1243-
oldsize = GetChunkSizeFromFreeListIdx(fidx);
1254+
oldchksize = GetChunkSizeFromFreeListIdx(fidx);
12441255

12451256
#ifdef MEMORY_CONTEXT_CHECKING
12461257
/* Test for someone scribbling on unused space in chunk */
1247-
if (chunk->requested_size < oldsize)
1258+
if (chunk->requested_size < oldchksize)
12481259
if (!sentinel_ok(pointer, chunk->requested_size))
12491260
elog(WARNING, "detected write past chunk end in %s %p",
12501261
set->header.name, chunk);
@@ -1255,7 +1266,7 @@ AllocSetRealloc(void *pointer, Size size)
12551266
* allocated area already is >= the new size. (In particular, we will
12561267
* fall out here if the requested size is a decrease.)
12571268
*/
1258-
if (oldsize >= size)
1269+
if (oldchksize >= size)
12591270
{
12601271
#ifdef MEMORY_CONTEXT_CHECKING
12611272
Size oldrequest = chunk->requested_size;
@@ -1278,10 +1289,10 @@ AllocSetRealloc(void *pointer, Size size)
12781289
size - oldrequest);
12791290
else
12801291
VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
1281-
oldsize - size);
1292+
oldchksize - size);
12821293

12831294
/* set mark to catch clobber of "unused" space */
1284-
if (size < oldsize)
1295+
if (size < oldchksize)
12851296
set_sentinel(pointer, size);
12861297
#else /* !MEMORY_CONTEXT_CHECKING */
12871298

@@ -1290,7 +1301,7 @@ AllocSetRealloc(void *pointer, Size size)
12901301
* the old request or shrinking it, so we conservatively mark the
12911302
* entire new allocation DEFINED.
12921303
*/
1293-
VALGRIND_MAKE_MEM_NOACCESS(pointer, oldsize);
1304+
VALGRIND_MAKE_MEM_NOACCESS(pointer, oldchksize);
12941305
VALGRIND_MAKE_MEM_DEFINED(pointer, size);
12951306
#endif
12961307

@@ -1313,6 +1324,7 @@ AllocSetRealloc(void *pointer, Size size)
13131324
* memory indefinitely. See pgsql-hackers archives for 2007-08-11.)
13141325
*/
13151326
AllocPointer newPointer;
1327+
Size oldsize;
13161328

13171329
/* allocate new chunk */
13181330
newPointer = AllocSetAlloc((MemoryContext) set, size);
@@ -1337,6 +1349,7 @@ AllocSetRealloc(void *pointer, Size size)
13371349
#ifdef MEMORY_CONTEXT_CHECKING
13381350
oldsize = chunk->requested_size;
13391351
#else
1352+
oldsize = oldchksize;
13401353
VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
13411354
#endif
13421355

0 commit comments

Comments
 (0)