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

Limit the depth of nested containers when reading JSON.

Each level of a nested array switches to a mem context in jsonReadVar() and each level of any container recurses, so deeply nested JSON could fill the mem context stack or exhaust the C stack. JSON is read from repository info files, manifests, lock files, and object store responses, which pgBackRest does not always control.

Error when the depth exceeds 32. The JSON pgBackRest reads does not nest more than a few levels.
This commit is contained in:
David Steele
2026-10-12 09:50:17 +02:00
parent 81704daaf4
commit 4b8e3f4386
2 changed files with 26 additions and 0 deletions

View File

@@ -11,8 +11,14 @@ Convert JSON to/from KeyValue
#include "common/debug.h"
#include "common/io/io.h"
#include "common/log.h"
#include "common/macro.h"
#include "common/type/json.h"
/***********************************************************************************************************************************
Maximum depth of nested containers when reading
***********************************************************************************************************************************/
#define JSON_READ_DEPTH_MAX 32
/***********************************************************************************************************************************
Object types
***********************************************************************************************************************************/
@@ -322,7 +328,12 @@ jsonReadPush(JsonRead *const this, const JsonType type, const bool key)
// Add container to the stack
if (jsonTypeContainer(type))
{
if (lstSize(this->stack) >= JSON_READ_DEPTH_MAX)
THROW_FMT(JsonFormatError, "maximum depth of " STRINGIFY(JSON_READ_DEPTH_MAX) " exceeded at: %s", this->json);
lstAdd(this->stack, &(JsonReadStack){.type = type});
}
FUNCTION_TEST_RETURN_VOID();
}

View File

@@ -61,6 +61,21 @@ testRun(void)
TEST_ERROR(jsonReadStr(jsonReadNew(STRDEF("\"\\u1111"))), JsonFormatError, "unable to decode at: \\u1111");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on maximum depth");
String *const depthArray = strNew();
String *const depthObject = strNew();
for (unsigned int depthIdx = 0; depthIdx < 32; depthIdx++)
{
strCatChr(depthArray, '[');
strCatZ(depthObject, "{\"a\":");
}
TEST_ERROR(jsonToVar(strNewFmt("%s{", strZ(depthArray))), JsonFormatError, "maximum depth of 32 exceeded at: {");
TEST_ERROR(jsonValidate(strNewFmt("%s[", strZ(depthObject))), JsonFormatError, "maximum depth of 32 exceeded at: [");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on invalid escape");