1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-10-23 21:28:56 +03:00

Check the mem context stack bounds in production builds.

memContextNew() added to the mem context stack without checking for space. memContextSwitch() asserted on the current stack index, which can be lower than the index being written, and the assert only existed in debug builds. Filling the stack overwrote the memory after it. Nested JSON arrays in a repository info file or an object store response can fill the stack because each level of an array switches to a mem context.

Check the max stack index in both functions with CHECK() so the error is also reported in production builds.
This commit is contained in:
David Steele
2026-10-12 09:32:10 +02:00
parent 80d46a0bef
commit 81704daaf4
2 changed files with 23 additions and 1 deletions

View File

@@ -749,6 +749,7 @@ memContextNew(
ASSERT(param.callbackQty <= 1);
// Check context name length
ASSERT(name[0] != '\0');
CHECK(AssertError, memContextMaxStackIdx < MEM_CONTEXT_STACK_MAX - 1, "mem context stack is full");
// Pad allocExtra so that optional structures will be aligned. There may not be any optional structures but it doesn't seem
// worth the cost of checking since memory allocations are aligned so the extra bytes would be wasted anyway.
@@ -1170,7 +1171,7 @@ memContextSwitch(MemContext *const this)
ASSERT(this != NULL);
ASSERT(this->active);
ASSERT(memContextCurrentStackIdx < MEM_CONTEXT_STACK_MAX - 1);
CHECK(AssertError, memContextMaxStackIdx < MEM_CONTEXT_STACK_MAX - 1, "mem context stack is full");
memContextMaxStackIdx++;
memContextCurrentStackIdx = memContextMaxStackIdx;

View File

@@ -25,6 +25,21 @@ testFree(void *thisVoid)
THROW(AssertError, "error in callback");
}
/***********************************************************************************************************************************
testStackFull - fill the stack with switches and then create or switch to a context
***********************************************************************************************************************************/
static void
testStackFull(MemContext *const memContext, const bool contextNew)
{
while (memContextMaxStackIdx < MEM_CONTEXT_STACK_MAX - 1)
memContextSwitch(memContext);
if (contextNew)
memContextNewP("test-full");
else
memContextSwitch(memContext);
}
/***********************************************************************************************************************************
Test Run
***********************************************************************************************************************************/
@@ -457,6 +472,12 @@ testRun(void)
TEST_RESULT_Z(memContextCurrent()->name, "TOP", "context is now top");
// Error when the stack is full
TEST_ERROR(testStackFull(memContext, false), AssertError, "mem context stack is full");
TEST_RESULT_Z(memContextCurrent()->name, "TOP", "context is now top");
TEST_ERROR(testStackFull(memContext, true), AssertError, "mem context stack is full");
TEST_RESULT_Z(memContextCurrent()->name, "TOP", "context is now top");
// Reset temp mem context after a single interaction
// -------------------------------------------------------------------------------------------------------------------------
MEM_CONTEXT_TEMP_RESET_BEGIN()