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

Fix reads past the end of JSON strings ending in an escape.

jsonReadSkipStr() stepped past the terminating null when a backslash was the last character. It also advanced one character too far after a \uXXXX escape, which skipped the closing quote when the escape was the last character of the string and stepped past the terminating null when the escape was at the end of the input. jsonReadStr() did not check that both digits of a \u00XX escape were present, so a truncated escape at the end of the input was read past the terminating null.

The skip path validates the JSON values in info files and manifests. The read path reads object store responses.
This commit is contained in:
David Steele
2026-10-12 10:10:20 +02:00
parent 4b8e3f4386
commit b623071a2a
2 changed files with 15 additions and 2 deletions

View File

@@ -838,11 +838,16 @@ jsonReadSkipStr(JsonRead *const this)
if (digitIdx != 4)
THROW_FMT(JsonFormatError, "unable to decode at: %s", this->json - 2);
this->json += 4;
// Move to the last digit
this->json += 3;
break;
}
// End of string after the escape
case '\0':
THROW(JsonFormatError, "expected '\"' but found null delimiter");
// Any other escape
default:
break;
@@ -1026,8 +1031,11 @@ jsonReadStr(JsonRead *const this)
this->json++;
// We don't know how to decode anything except ASCII so fail if it looks like Unicode
if (strncmp(this->json, "00", 2) != 0)
if (strncmp(this->json, "00", 2) != 0 || !isxdigit((unsigned char)this->json[2]) ||
!isxdigit((unsigned char)this->json[3]))
{
THROW_FMT(JsonFormatError, "unable to decode at: %s", this->json - 2);
}
// Decode char
this->json += 2;

View File

@@ -60,6 +60,8 @@ testRun(void)
TEST_TITLE("error on invalid ascii encoding");
TEST_ERROR(jsonReadStr(jsonReadNew(STRDEF("\"\\u1111"))), JsonFormatError, "unable to decode at: \\u1111");
TEST_ERROR(jsonReadStr(jsonReadNew(STRDEF("\"\\u00x1"))), JsonFormatError, "unable to decode at: \\u00x1");
TEST_ERROR(jsonReadStr(jsonReadNew(STRDEF("\"\\u00a"))), JsonFormatError, "unable to decode at: \\u00a");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on maximum depth");
@@ -229,8 +231,11 @@ testRun(void)
TEST_TITLE("validate");
TEST_RESULT_VOID(jsonValidate(json), "valid");
TEST_RESULT_VOID(jsonValidate(STRDEF("[\"\\u0041\",\"\\u1234\"]")), "valid escapes before end of string");
TEST_ERROR(jsonValidate(STRDEF("\"\\u0041")), JsonFormatError, "expected '\"' but found null delimiter");
TEST_ERROR(jsonValidate(STRDEF("\"\\u000")), JsonFormatError, "unable to decode at: \\u000");
TEST_ERROR(jsonValidate(STRDEF("\"")), JsonFormatError, "expected '\"' but found null delimiter");
TEST_ERROR(jsonValidate(STRDEF("\"\\")), JsonFormatError, "expected '\"' but found null delimiter");
TEST_ERROR(jsonValidate(STRDEF("{\"key\"x")), JsonFormatError, "expected : after key at: x");
TEST_ERROR(jsonValidate(strNewFmt("%s]", strZ(json))), JsonFormatError, "characters after JSON at: ]");
}