From 81704daaf41882104b7545ece6905258c1bc23a7 Mon Sep 17 00:00:00 2001 From: David Steele Date: Mon, 12 Oct 2026 09:32:10 +0200 Subject: [PATCH] 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. --- src/common/memContext.c | 3 ++- test/src/module/common/memContextTest.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/common/memContext.c b/src/common/memContext.c index ced60451e..f39810989 100644 --- a/src/common/memContext.c +++ b/src/common/memContext.c @@ -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; diff --git a/test/src/module/common/memContextTest.c b/test/src/module/common/memContextTest.c index 0e6a93a20..899c78c6c 100644 --- a/test/src/module/common/memContextTest.c +++ b/test/src/module/common/memContextTest.c @@ -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()