diff --git a/extras/tests/JsonDeserializer/errors.cpp b/extras/tests/JsonDeserializer/errors.cpp index 9bd5681c..20cc5118 100644 --- a/extras/tests/JsonDeserializer/errors.cpp +++ b/extras/tests/JsonDeserializer/errors.cpp @@ -101,3 +101,20 @@ TEST_CASE("deserializeJson() returns EmptyInput") { REQUIRE(err == DeserializationError::EmptyInput); } } + +TEST_CASE("deserializeJson() returns NoMemory if string length overflows") { + JsonDocument doc; + auto maxLength = ArduinoJson::detail::StringNode::maxLength; + + SECTION("max length should succeed") { + auto err = deserializeJson(doc, "\"" + std::string(maxLength, 'a') + "\""); + + REQUIRE(err == DeserializationError::Ok); + } + + SECTION("one above max length should fail") { + auto err = + deserializeJson(doc, "\"" + std::string(maxLength + 1, 'a') + "\""); + REQUIRE(err == DeserializationError::NoMemory); + } +} diff --git a/extras/tests/JsonDocument/overflowed.cpp b/extras/tests/JsonDocument/overflowed.cpp index 25fb03a6..6253245e 100644 --- a/extras/tests/JsonDocument/overflowed.cpp +++ b/extras/tests/JsonDocument/overflowed.cpp @@ -80,4 +80,16 @@ TEST_CASE("JsonDocument::overflowed()") { doc.shrinkToFit(); CHECK(doc.overflowed() == true); } + + SECTION("returns false when string length doesn't overflow") { + auto maxLength = ArduinoJson::detail::StringNode::maxLength; + CHECK(doc.set(std::string(maxLength, 'a')) == true); + CHECK(doc.overflowed() == false); + } + + SECTION("returns true when string length overflows") { + auto maxLength = ArduinoJson::detail::StringNode::maxLength; + CHECK(doc.set(std::string(maxLength + 1, 'a')) == false); + CHECK(doc.overflowed() == true); + } } diff --git a/extras/tests/MsgPackDeserializer/errors.cpp b/extras/tests/MsgPackDeserializer/errors.cpp index 0ce7f0a8..da9380ac 100644 --- a/extras/tests/MsgPackDeserializer/errors.cpp +++ b/extras/tests/MsgPackDeserializer/errors.cpp @@ -240,3 +240,26 @@ TEST_CASE("deserializeMsgPack() replaces unsupported types by null") { 20) == "[null,42]"); } } + +TEST_CASE("deserializeMsgPack() returns NoMemory is string length overflows") { + JsonDocument doc; + auto maxLength = ArduinoJson::detail::StringNode::maxLength; + + SECTION("max length should succeed") { + auto len = maxLength; + std::string prefix = {'\xdb', char(len >> 24), char(len >> 16), + char(len >> 8), char(len)}; + + auto err = deserializeMsgPack(doc, prefix + std::string(len, 'a')); + REQUIRE(err == DeserializationError::Ok); + } + + SECTION("one above max length should fail") { + auto len = maxLength + 1; + std::string prefix = {'\xdb', char(len >> 24), char(len >> 16), + char(len >> 8), char(len)}; + + auto err = deserializeMsgPack(doc, prefix + std::string(len, 'a')); + REQUIRE(err == DeserializationError::NoMemory); + } +} diff --git a/src/ArduinoJson/Memory/StringNode.hpp b/src/ArduinoJson/Memory/StringNode.hpp index 06dcba09..4f35e492 100644 --- a/src/ArduinoJson/Memory/StringNode.hpp +++ b/src/ArduinoJson/Memory/StringNode.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include // offsetof #include // uint16_t @@ -19,11 +20,15 @@ struct StringNode { uint16_t references; char data[1]; + static constexpr size_t maxLength = numeric_limits::highest(); + static constexpr size_t sizeForLength(size_t n) { return n + 1 + offsetof(StringNode, data); } static StringNode* create(size_t length, Allocator* allocator) { + if (length > maxLength) + return nullptr; auto node = reinterpret_cast( allocator->allocate(sizeForLength(length))); if (node) { @@ -36,8 +41,12 @@ struct StringNode { static StringNode* resize(StringNode* node, size_t length, Allocator* allocator) { ARDUINOJSON_ASSERT(node != nullptr); - auto newNode = reinterpret_cast( - allocator->reallocate(node, sizeForLength(length))); + StringNode* newNode; + if (length <= maxLength) + newNode = reinterpret_cast( + allocator->reallocate(node, sizeForLength(length))); + else + newNode = nullptr; if (newNode) newNode->length = uint16_t(length); else diff --git a/src/ArduinoJson/Polyfills/limits.hpp b/src/ArduinoJson/Polyfills/limits.hpp index cf799da6..ab4ea136 100644 --- a/src/ArduinoJson/Polyfills/limits.hpp +++ b/src/ArduinoJson/Polyfills/limits.hpp @@ -19,10 +19,10 @@ struct numeric_limits; template struct numeric_limits::value>::type> { - static T lowest() { + static constexpr T lowest() { return 0; } - static T highest() { + static constexpr T highest() { return T(-1); } }; @@ -30,10 +30,10 @@ struct numeric_limits::value>::type> { template struct numeric_limits< T, typename enable_if::value && is_signed::value>::type> { - static T lowest() { + static constexpr T lowest() { return T(T(1) << (sizeof(T) * 8 - 1)); } - static T highest() { + static constexpr T highest() { return T(~lowest()); } };