Skip to content

Commit 5265e91

Browse files
committed
Make MemoryContextContains work correctly again
c6e0fe1 recently changed the way we store headers for allocated chunks of memory. Prior to that commit, we stored a pointer to the owning MemoryContext directly prior to the pointer to the allocated memory. That's no longer true and c6e0fe1 neglected to update MemoryContextContains() so that it correctly obtains the owning context with the new method. A side effect of this change and c6e0fe1, in general, is that it's even less safe than it was previously to pass MemoryContextContains() an arbitrary pointer which was not allocated by one of our MemoryContexts. Previously some comments in MemoryContextContains() seemed to indicate that the worst that could happen by passing an arbitrary pointer would be a false positive return value. It seems to me that this was a rather wishful outlook as we subsequently proceeded to subtract sizeof(void *) from the given pointer and then dereferenced that memory. So it seems quite likely that we could have segfaulted instead of returning a false positive. However, it's not impossible that the memory sizeof(void *) bytes before the pointer could have been owned by the process, but it's far less likely to work now as obtaining a pointer to the owning MemoryContext is less direct than before c6e0fe1 and will access memory that's possibly much further away to obtain the owning MemoryContext. Because of this, I took the liberty of updating the comment to warn against any future usages of the function and checked the existing core usages to ensure that we only ever pass in a pointer to memory allocated by a MemoryContext. Extension authors updating their code for PG16 who are using MemoryContextContains should check to ensure that only NULL pointers and pointers to chunks allocated with a MemoryContext will ever be passed to MemoryContextContains. Reported-by: Andres Freund Discussion: https://postgr.es/m/20220905230949.kb3x2fkpfwtngz43@awork3.anarazel.de
1 parent 3fe76ab commit 5265e91

File tree

1 file changed

+36
-9
lines changed

1 file changed

+36
-9
lines changed

src/backend/utils/mmgr/mcxt.c

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,15 @@ MemoryContextAllowInCriticalSection(MemoryContext context, bool allow)
482482
MemoryContext
483483
GetMemoryChunkContext(void *pointer)
484484
{
485+
/*
486+
* Try to detect bogus pointers handed to us, poorly though we can.
487+
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
488+
* allocated chunk.
489+
*/
490+
Assert(pointer != NULL);
491+
Assert(pointer == (void *) MAXALIGN(pointer));
492+
/* adding further Asserts here? See pre-checks in MemoryContextContains */
493+
485494
return MCXT_METHOD(pointer, get_chunk_context) (pointer);
486495
}
487496

@@ -809,21 +818,19 @@ MemoryContextCheck(MemoryContext context)
809818
* Detect whether an allocated chunk of memory belongs to a given
810819
* context or not.
811820
*
812-
* Caution: this test is reliable as long as 'pointer' does point to
813-
* a chunk of memory allocated from *some* context. If 'pointer' points
814-
* at memory obtained in some other way, there is a small chance of a
815-
* false-positive result, since the bits right before it might look like
816-
* a valid chunk header by chance.
821+
* Caution: 'pointer' must point to a pointer which was allocated by a
822+
* MemoryContext. It's not safe or valid to use this function on arbitrary
823+
* pointers as obtaining the MemoryContext which 'pointer' belongs to requires
824+
* possibly several pointer dereferences.
817825
*/
818826
bool
819827
MemoryContextContains(MemoryContext context, void *pointer)
820828
{
821829
MemoryContext ptr_context;
822830

823831
/*
824-
* NB: Can't use GetMemoryChunkContext() here - that performs assertions
825-
* that aren't acceptable here since we might be passed memory not
826-
* allocated by any memory context.
832+
* NB: We must perform run-time checks here which GetMemoryChunkContext()
833+
* does as assertions before calling GetMemoryChunkContext().
827834
*
828835
* Try to detect bogus pointers handed to us, poorly though we can.
829836
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
@@ -835,7 +842,7 @@ MemoryContextContains(MemoryContext context, void *pointer)
835842
/*
836843
* OK, it's probably safe to look at the context.
837844
*/
838-
ptr_context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
845+
ptr_context = GetMemoryChunkContext(pointer);
839846

840847
return ptr_context == context;
841848
}
@@ -953,6 +960,8 @@ MemoryContextAlloc(MemoryContext context, Size size)
953960

954961
VALGRIND_MEMPOOL_ALLOC(context, ret, size);
955962

963+
Assert(MemoryContextContains(context, ret));
964+
956965
return ret;
957966
}
958967

@@ -991,6 +1000,8 @@ MemoryContextAllocZero(MemoryContext context, Size size)
9911000

9921001
MemSetAligned(ret, 0, size);
9931002

1003+
Assert(MemoryContextContains(context, ret));
1004+
9941005
return ret;
9951006
}
9961007

@@ -1029,6 +1040,8 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size)
10291040

10301041
MemSetLoop(ret, 0, size);
10311042

1043+
Assert(MemoryContextContains(context, ret));
1044+
10321045
return ret;
10331046
}
10341047

@@ -1070,6 +1083,8 @@ MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
10701083
if ((flags & MCXT_ALLOC_ZERO) != 0)
10711084
MemSetAligned(ret, 0, size);
10721085

1086+
Assert(MemoryContextContains(context, ret));
1087+
10731088
return ret;
10741089
}
10751090

@@ -1153,6 +1168,8 @@ palloc(Size size)
11531168

11541169
VALGRIND_MEMPOOL_ALLOC(context, ret, size);
11551170

1171+
Assert(MemoryContextContains(context, ret));
1172+
11561173
return ret;
11571174
}
11581175

@@ -1186,6 +1203,8 @@ palloc0(Size size)
11861203

11871204
MemSetAligned(ret, 0, size);
11881205

1206+
Assert(MemoryContextContains(context, ret));
1207+
11891208
return ret;
11901209
}
11911210

@@ -1225,6 +1244,8 @@ palloc_extended(Size size, int flags)
12251244
if ((flags & MCXT_ALLOC_ZERO) != 0)
12261245
MemSetAligned(ret, 0, size);
12271246

1247+
Assert(MemoryContextContains(context, ret));
1248+
12281249
return ret;
12291250
}
12301251

@@ -1278,6 +1299,8 @@ repalloc(void *pointer, Size size)
12781299

12791300
VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
12801301

1302+
Assert(MemoryContextContains(context, ret));
1303+
12811304
return ret;
12821305
}
12831306

@@ -1313,6 +1336,8 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
13131336

13141337
VALGRIND_MEMPOOL_ALLOC(context, ret, size);
13151338

1339+
Assert(MemoryContextContains(context, ret));
1340+
13161341
return ret;
13171342
}
13181343

@@ -1352,6 +1377,8 @@ repalloc_huge(void *pointer, Size size)
13521377

13531378
VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
13541379

1380+
Assert(MemoryContextContains(context, ret));
1381+
13551382
return ret;
13561383
}
13571384

0 commit comments

Comments
 (0)