make WeaselJson_OVERFLOW a terminal state like WeaselJson_REJECT
CI / pre-commit (pull_request) Successful in 53s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 54s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m35s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m32s
CI / pre-commit (pull_request) Successful in 53s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 54s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m35s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m32s
When a parse step returned WeaselJson_OVERFLOW, the pushdown stack was left corrupted (frames had been popped before the failing push), but the overflow status was not made sticky the way WeaselJson_REJECT is. A subsequent end-of-data call WeaselJsonParser_parse(parser, nullptr, 0) could then dispatch through the corrupted stack and return WeaselJson_OK, accepting an incomplete, invalid too-deeply-nested document as valid JSON. Add an `overflowed` flag, set it whenever a continuation returns WeaselJson_OVERFLOW, and short-circuit parse() to return WeaselJson_OVERFLOW on every subsequent call. reset() clears the flag so a reused parser can accept a different document. This mirrors the existing stickiness handling for WeaselJson_REJECT. Closes #52
This commit is contained in:
+9
-9
@@ -139,7 +139,7 @@ struct Parser3 {
|
||||
stackPtr = stack();
|
||||
std::ignore = push({N_VALUE, N_WHITESPACE, T_EOF});
|
||||
inKey = false;
|
||||
rejected = false;
|
||||
terminalStatus = WeaselJson_OK;
|
||||
utf8Codepoint = 0;
|
||||
utf16Surrogate = 0;
|
||||
minCodepoint = 0;
|
||||
@@ -162,7 +162,7 @@ struct Parser3 {
|
||||
NumDfa numDfa;
|
||||
Utf8Dfa strDfa;
|
||||
bool inKey = false;
|
||||
bool rejected = false;
|
||||
WeaselJsonStatus terminalStatus = WeaselJson_OK;
|
||||
|
||||
#ifndef HAS_MUSTTAIL
|
||||
char *stashBufForTrampoline;
|
||||
@@ -1070,12 +1070,12 @@ constexpr inline struct ContinuationTable {
|
||||
inline WeaselJsonStatus Parser3::parse(char *buf, int len) {
|
||||
this->dataBegin = this->writeBuf = buf;
|
||||
|
||||
if (this->rejected) [[unlikely]] {
|
||||
return WeaselJson_REJECT;
|
||||
if (this->terminalStatus != WeaselJson_OK) [[unlikely]] {
|
||||
return this->terminalStatus;
|
||||
}
|
||||
|
||||
if (len < 0) [[unlikely]] {
|
||||
this->rejected = true;
|
||||
this->terminalStatus = WeaselJson_REJECT;
|
||||
return WeaselJson_REJECT;
|
||||
}
|
||||
|
||||
@@ -1085,8 +1085,8 @@ inline WeaselJsonStatus Parser3::parse(char *buf, int len) {
|
||||
// range.
|
||||
ContinuationStatus status =
|
||||
symbolTables.continuations[top()](this, buf, buf + len);
|
||||
if (status == WeaselJson_REJECT) {
|
||||
this->rejected = true;
|
||||
if (status == WeaselJson_REJECT || status == WeaselJson_OVERFLOW) {
|
||||
this->terminalStatus = WeaselJsonStatus(status);
|
||||
}
|
||||
return WeaselJsonStatus(status);
|
||||
#else
|
||||
@@ -1095,8 +1095,8 @@ inline WeaselJsonStatus Parser3::parse(char *buf, int len) {
|
||||
while ((result = symbolTables.continuations[top()](
|
||||
this, stashBufForTrampoline, buf + len)) == kBounce)
|
||||
;
|
||||
if (result == WeaselJson_REJECT) {
|
||||
this->rejected = true;
|
||||
if (result == WeaselJson_REJECT || result == WeaselJson_OVERFLOW) {
|
||||
this->terminalStatus = WeaselJsonStatus(result);
|
||||
}
|
||||
return WeaselJsonStatus(result);
|
||||
#endif
|
||||
|
||||
@@ -202,6 +202,62 @@ TEST_CASE("parser3") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("overflow state is sticky") {
|
||||
auto c = noopCallbacks();
|
||||
// stackSize 3 is exactly big enough to hold reset()'s bootstrap, but too
|
||||
// small for nested arrays. Overflows must be terminal like rejects: a later
|
||||
// end-of-data call must never report OK for an incomplete document.
|
||||
auto *parser = WeaselJsonParser_create(3, &c, nullptr, 0);
|
||||
REQUIRE(parser != nullptr);
|
||||
|
||||
std::string doc = "[[";
|
||||
REQUIRE(WeaselJsonParser_parse(parser, doc.data(), doc.size()) ==
|
||||
WeaselJson_OVERFLOW);
|
||||
|
||||
// After overflow, the end-of-data call must not return OK.
|
||||
REQUIRE(WeaselJsonParser_parse(parser, nullptr, 0) != WeaselJson_OK);
|
||||
// It should keep reporting a terminal failure.
|
||||
REQUIRE(WeaselJsonParser_parse(parser, nullptr, 0) != WeaselJson_OK);
|
||||
|
||||
// Further data chunks must also stay terminal.
|
||||
std::string more = "]]";
|
||||
REQUIRE(WeaselJsonParser_parse(parser, more.data(), more.size()) !=
|
||||
WeaselJson_OK);
|
||||
|
||||
WeaselJsonParser_destroy(parser);
|
||||
}
|
||||
|
||||
TEST_CASE("overflow is sticky for nested objects") {
|
||||
auto c = noopCallbacks();
|
||||
auto *parser = WeaselJsonParser_create(4, &c, nullptr, 0);
|
||||
REQUIRE(parser != nullptr);
|
||||
|
||||
std::string doc = "{\"a\":{ \"a\":";
|
||||
REQUIRE(WeaselJsonParser_parse(parser, doc.data(), doc.size()) ==
|
||||
WeaselJson_OVERFLOW);
|
||||
REQUIRE(WeaselJsonParser_parse(parser, nullptr, 0) != WeaselJson_OK);
|
||||
|
||||
WeaselJsonParser_destroy(parser);
|
||||
}
|
||||
|
||||
TEST_CASE("reset clears overflow state") {
|
||||
auto c = noopCallbacks();
|
||||
auto *parser = WeaselJsonParser_create(3, &c, nullptr, 0);
|
||||
REQUIRE(parser != nullptr);
|
||||
|
||||
std::string doc = "[[";
|
||||
REQUIRE(WeaselJsonParser_parse(parser, doc.data(), doc.size()) ==
|
||||
WeaselJson_OVERFLOW);
|
||||
// After reset the parser should accept a minimal document again.
|
||||
WeaselJsonParser_reset(parser);
|
||||
std::string copy = "1";
|
||||
REQUIRE(WeaselJsonParser_parse(parser, copy.data(), copy.size()) ==
|
||||
WeaselJson_AGAIN);
|
||||
REQUIRE(WeaselJsonParser_parse(parser, nullptr, 0) == WeaselJson_OK);
|
||||
|
||||
WeaselJsonParser_destroy(parser);
|
||||
}
|
||||
|
||||
TEST_CASE("rejected state is sticky") {
|
||||
auto c = noopCallbacks();
|
||||
auto *parser = WeaselJsonParser_create(1024, &c, nullptr, 0);
|
||||
|
||||
Reference in New Issue
Block a user